diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/content_renderer.gypi | 12 | ||||
-rw-r--r-- | content/renderer/content_renderer_client.cc | 4 | ||||
-rw-r--r-- | content/renderer/content_renderer_client.h | 3 | ||||
-rw-r--r-- | content/renderer/load_progress_tracker.cc | 86 | ||||
-rw-r--r-- | content/renderer/load_progress_tracker.h | 45 | ||||
-rw-r--r-- | content/renderer/navigation_state.cc | 80 | ||||
-rw-r--r-- | content/renderer/navigation_state.h | 338 | ||||
-rw-r--r-- | content/renderer/paint_aggregator.cc | 284 | ||||
-rw-r--r-- | content/renderer/paint_aggregator.h | 64 | ||||
-rw-r--r-- | content/renderer/paint_aggregator_unittest.cc | 437 | ||||
-rw-r--r-- | content/renderer/pepper_platform_context_3d_impl.cc | 114 | ||||
-rw-r--r-- | content/renderer/pepper_platform_context_3d_impl.h | 56 | ||||
-rw-r--r-- | content/renderer/pepper_plugin_delegate_impl.cc | 958 | ||||
-rw-r--r-- | content/renderer/pepper_plugin_delegate_impl.h | 202 | ||||
-rw-r--r-- | content/renderer/web_ui_bindings.cc | 99 | ||||
-rw-r--r-- | content/renderer/web_ui_bindings.h | 68 |
16 files changed, 2850 insertions, 0 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 6fa2543..7dd7849 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -43,12 +43,16 @@ 'renderer/gpu_video_service_host.h', 'renderer/indexed_db_dispatcher.cc', 'renderer/indexed_db_dispatcher.h', + 'renderer/load_progress_tracker.cc', + 'renderer/load_progress_tracker.h', 'renderer/media/audio_renderer_impl.cc', 'renderer/media/audio_renderer_impl.h', 'renderer/media/gles2_video_decode_context.cc', 'renderer/media/gles2_video_decode_context.h', 'renderer/media/ipc_video_decoder.cc', 'renderer/media/ipc_video_decoder.h', + 'renderer/navigation_state.cc', + 'renderer/navigation_state.h', 'renderer/notification_provider.cc', 'renderer/notification_provider.h', 'renderer/p2p/ipc_network_manager.cc', @@ -59,6 +63,12 @@ 'renderer/p2p/socket_client.h', 'renderer/p2p/socket_dispatcher.cc', 'renderer/p2p/socket_dispatcher.h', + 'renderer/paint_aggregator.cc', + 'renderer/paint_aggregator.h', + 'renderer/pepper_platform_context_3d_impl.cc', + 'renderer/pepper_platform_context_3d_impl.h', + 'renderer/pepper_plugin_delegate_impl.cc', + 'renderer/pepper_plugin_delegate_impl.h', 'renderer/plugin_channel_host.cc', 'renderer/plugin_channel_host.h', 'renderer/speech_input_dispatcher.cc', @@ -75,6 +85,8 @@ 'renderer/webworker_base.h', 'renderer/webworker_proxy.cc', 'renderer/webworker_proxy.h', + 'renderer/web_ui_bindings.cc', + 'renderer/web_ui_bindings.h', ], 'conditions': [ ['enable_gpu==1', { diff --git a/content/renderer/content_renderer_client.cc b/content/renderer/content_renderer_client.cc index 56b2568..a247b3c 100644 --- a/content/renderer/content_renderer_client.cc +++ b/content/renderer/content_renderer_client.cc @@ -10,4 +10,8 @@ SkBitmap* ContentRendererClient::GetSadPluginBitmap() { return NULL; } +std::string ContentRendererClient::GetDefaultEncoding() { + return std::string(); +} + } // namespace content diff --git a/content/renderer/content_renderer_client.h b/content/renderer/content_renderer_client.h index 557d858..dac0af8 100644 --- a/content/renderer/content_renderer_client.h +++ b/content/renderer/content_renderer_client.h @@ -6,6 +6,8 @@ #define CONTENT_RENDERER_CONTENT_RENDERER_CLIENT_H_ #pragma once +#include <string> + #include "content/common/content_client.h" class SkBitmap; @@ -16,6 +18,7 @@ namespace content { class ContentRendererClient { public: virtual SkBitmap* GetSadPluginBitmap(); + virtual std::string GetDefaultEncoding(); }; } // namespace content diff --git a/content/renderer/load_progress_tracker.cc b/content/renderer/load_progress_tracker.cc new file mode 100644 index 0000000..aa384f6 --- /dev/null +++ b/content/renderer/load_progress_tracker.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/load_progress_tracker.h" + +#include "base/message_loop.h" +#include "chrome/common/render_messages.h" +#include "chrome/renderer/render_view.h" + +namespace { + +const int kMinimumDelayBetweenUpdatesMS = 100; + +} + +LoadProgressTracker::LoadProgressTracker(RenderView* render_view) + : render_view_(render_view), + tracked_frame_(NULL), + progress_(0.0), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { +} + +LoadProgressTracker::~LoadProgressTracker() { +} + +void LoadProgressTracker::DidStopLoading() { + if (!tracked_frame_) + return; + + // Load stoped while we were still tracking load. Make sure we notify the + // browser that load is complete. + progress_ = 1.0; + SendChangeLoadProgress(); + // Then we clean-up our states. + ResetStates(); +} + +void LoadProgressTracker::DidChangeLoadProgress(WebKit::WebFrame* frame, + double progress) { + if (tracked_frame_ && frame != tracked_frame_) + return; + + if (!tracked_frame_) + tracked_frame_ = frame; + + progress_ = progress; + + // We send the progress change to the browser immediately for the first and + // last updates. Also, since the message loop may be pretty busy when a page + // is loaded, it might not execute a posted task in a timely manner so we make + // sure to immediately send progress report if enough time has passed. + if (progress == 1.0 || last_time_progress_sent_.is_null() || + (base::TimeTicks::Now() - last_time_progress_sent_).InMilliseconds() > + kMinimumDelayBetweenUpdatesMS) { + // If there is a pending task to send progress, it is now obsolete. + method_factory_.RevokeAll(); + SendChangeLoadProgress(); + if (progress == 1.0) + ResetStates(); + return; + } + + if (!method_factory_.empty()) + return; + + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &LoadProgressTracker::SendChangeLoadProgress), + kMinimumDelayBetweenUpdatesMS); +} + +void LoadProgressTracker::SendChangeLoadProgress() { + last_time_progress_sent_ = base::TimeTicks::Now(); + render_view_->Send( + new ViewHostMsg_DidChangeLoadProgress(render_view_->routing_id(), + progress_)); +} + +void LoadProgressTracker::ResetStates() { + tracked_frame_ = NULL; + progress_ = 0.0; + method_factory_.RevokeAll(); + last_time_progress_sent_ = base::TimeTicks(); +} diff --git a/content/renderer/load_progress_tracker.h b/content/renderer/load_progress_tracker.h new file mode 100644 index 0000000..490bd82 --- /dev/null +++ b/content/renderer/load_progress_tracker.h @@ -0,0 +1,45 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_LOAD_PROGRESS_TRACKER_H_ +#define CONTENT_RENDERER_LOAD_PROGRESS_TRACKER_H_ + +#include "base/logging.h" +#include "base/task.h" +#include "base/time.h" + +class RenderView; + +namespace WebKit { +class WebFrame; +} + +class LoadProgressTracker { + public: + explicit LoadProgressTracker(RenderView* render_view); + ~LoadProgressTracker(); + + void DidStopLoading(); + + void DidChangeLoadProgress(WebKit::WebFrame* frame, double progress); + + private: + void ResetStates(); + + void SendChangeLoadProgress(); + + RenderView* render_view_; + + WebKit::WebFrame* tracked_frame_; + + double progress_; + + base::TimeTicks last_time_progress_sent_; + + ScopedRunnableMethodFactory<LoadProgressTracker> method_factory_; + + DISALLOW_COPY_AND_ASSIGN(LoadProgressTracker); +}; + +#endif // CONTENT_RENDERER_LOAD_PROGRESS_TRACKER_H_ diff --git a/content/renderer/navigation_state.cc b/content/renderer/navigation_state.cc new file mode 100644 index 0000000..78bebbb --- /dev/null +++ b/content/renderer/navigation_state.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/navigation_state.h" + +#include "chrome/renderer/user_script_idle_scheduler.h" +#include "webkit/glue/alt_error_page_resource_fetcher.h" +#include "webkit/glue/password_form.h" + +NavigationState::~NavigationState() {} + +void NavigationState::set_user_script_idle_scheduler( + UserScriptIdleScheduler* scheduler) { + user_script_idle_scheduler_.reset(scheduler); +} + +void NavigationState::swap_user_script_idle_scheduler( + NavigationState* state) { + user_script_idle_scheduler_.swap(state->user_script_idle_scheduler_); +} + +const base::Time& NavigationState::prerendered_page_display_time() const { + return prerendered_page_display_time_; +} + +void NavigationState::set_prerendered_page_display_time( + const base::Time& value) { + DCHECK(prerendered_page_display_time_.is_null()); + prerendered_page_display_time_ = value; +} + +void NavigationState::set_password_form_data(webkit_glue::PasswordForm* data) { + password_form_data_.reset(data); +} + +void NavigationState::set_alt_error_page_fetcher( + webkit_glue::AltErrorPageResourceFetcher* f) { + alt_error_page_fetcher_.reset(f); +} + +bool NavigationState::was_started_as_prerender() const { + return was_started_as_prerender_; +} + +void NavigationState::set_was_started_as_prerender( + bool was_started_as_prerender) { + DCHECK(!was_started_as_prerender_); + was_started_as_prerender_ = was_started_as_prerender; +} + +NavigationState::NavigationState(PageTransition::Type transition_type, + const base::Time& request_time, + bool is_content_initiated, + int32 pending_page_id, + int pending_history_list_offset) + : transition_type_(transition_type), + load_type_(UNDEFINED_LOAD), + request_time_(request_time), + load_histograms_recorded_(false), + web_timing_histograms_recorded_(false), + request_committed_(false), + is_content_initiated_(is_content_initiated), + pending_page_id_(pending_page_id), + pending_history_list_offset_(pending_history_list_offset), + use_error_page_(false), + was_started_as_prerender_(false), + cache_policy_override_set_(false), + cache_policy_override_(WebKit::WebURLRequest::UseProtocolCachePolicy), + user_script_idle_scheduler_(NULL), + http_status_code_(0), + was_fetched_via_spdy_(false), + was_npn_negotiated_(false), + was_alternate_protocol_available_(false), + was_fetched_via_proxy_(false), + was_translated_(false), + was_within_same_page_(false), + was_prefetcher_(false), + was_referred_by_prefetcher_(false) { +} diff --git a/content/renderer/navigation_state.h b/content/renderer/navigation_state.h new file mode 100644 index 0000000..8941542 --- /dev/null +++ b/content/renderer/navigation_state.h @@ -0,0 +1,338 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_NAVIGATION_STATE_H_ +#define CHROME_RENDERER_NAVIGATION_STATE_H_ +#pragma once + +#include <string> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "chrome/common/extensions/url_pattern.h" +#include "content/common/page_transition_types.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h" + +namespace webkit_glue { +struct PasswordForm; +class AltErrorPageResourceFetcher; +} + +class UserScriptIdleScheduler; + +// The RenderView stores an instance of this class in the "extra data" of each +// WebDataSource (see RenderView::DidCreateDataSource). +class NavigationState : public WebKit::WebDataSource::ExtraData { + public: + // The exact values of this enum are used in histograms, so new values must be + // added to the end. + enum LoadType { + UNDEFINED_LOAD, // Not yet initialized. + RELOAD, // User pressed reload. + HISTORY_LOAD, // Back or forward. + NORMAL_LOAD, // User entered URL, or omnibox search. + LINK_LOAD, // (deprecated) Included next 4 categories. + LINK_LOAD_NORMAL, // Commonly following of link. + LINK_LOAD_RELOAD, // JS/link directed reload. + LINK_LOAD_CACHE_STALE_OK, // back/forward or encoding change. + LINK_LOAD_CACHE_ONLY, // Allow stale data (avoid doing a re-post) + PRERENDER_LOAD, // Navigation started as the speculative + // prendering of a linked page. + kLoadTypeMax // Bounding value for this enum. + }; + + virtual ~NavigationState(); + + static NavigationState* CreateBrowserInitiated( + int32 pending_page_id, + int pending_history_list_offset, + PageTransition::Type transition_type, + base::Time request_time) { + return new NavigationState(transition_type, request_time, false, + pending_page_id, + pending_history_list_offset); + } + + static NavigationState* CreateContentInitiated() { + // We assume navigations initiated by content are link clicks. + return new NavigationState(PageTransition::LINK, base::Time(), true, -1, + -1); + } + + static NavigationState* FromDataSource(WebKit::WebDataSource* ds) { + return static_cast<NavigationState*>(ds->extraData()); + } + + UserScriptIdleScheduler* user_script_idle_scheduler() { + return user_script_idle_scheduler_.get(); + } + void set_user_script_idle_scheduler(UserScriptIdleScheduler* scheduler); + void swap_user_script_idle_scheduler(NavigationState* state); + + // Contains the page_id for this navigation or -1 if there is none yet. + int32 pending_page_id() const { return pending_page_id_; } + + // If pending_page_id() is not -1, then this contains the corresponding + // offset of the page in the back/forward history list. + int pending_history_list_offset() const { + return pending_history_list_offset_; + } + + // Contains the transition type that the browser specified when it + // initiated the load. + PageTransition::Type transition_type() const { return transition_type_; } + void set_transition_type(PageTransition::Type type) { + transition_type_ = type; + } + + // Record the nature of this load, for use when histogramming page load times. + LoadType load_type() const { return load_type_; } + void set_load_type(LoadType load_type) { load_type_ = load_type; } + + // The time that this navigation was requested. + const base::Time& request_time() const { + return request_time_; + } + void set_request_time(const base::Time& value) { + DCHECK(start_load_time_.is_null()); + request_time_ = value; + } + + // The time that the document load started. + const base::Time& start_load_time() const { + return start_load_time_; + } + void set_start_load_time(const base::Time& value) { + // TODO(jar): This should not be set twice. + // DCHECK(!start_load_time_.is_null()); + DCHECK(finish_document_load_time_.is_null()); + start_load_time_ = value; + } + + // The time that the document load was committed. + const base::Time& commit_load_time() const { + return commit_load_time_; + } + void set_commit_load_time(const base::Time& value) { + commit_load_time_ = value; + } + + // The time that the document finished loading. + const base::Time& finish_document_load_time() const { + return finish_document_load_time_; + } + void set_finish_document_load_time(const base::Time& value) { + // TODO(jar): Some unittests break the following DCHECK, and don't have + // DCHECK(!start_load_time_.is_null()); + DCHECK(!value.is_null()); + // TODO(jar): Double setting does happen, but probably shouldn't. + // DCHECK(finish_document_load_time_.is_null()); + // TODO(jar): We should guarantee this order :-(. + // DCHECK(finish_load_time_.is_null()); + finish_document_load_time_ = value; + } + + // The time that the document and all subresources finished loading. + const base::Time& finish_load_time() const { return finish_load_time_; } + void set_finish_load_time(const base::Time& value) { + DCHECK(!value.is_null()); + DCHECK(finish_load_time_.is_null()); + // The following is not already set in all cases :-( + // DCHECK(!finish_document_load_time_.is_null()); + finish_load_time_ = value; + } + + // The time that painting first happened after a new navigation. + const base::Time& first_paint_time() const { return first_paint_time_; } + void set_first_paint_time(const base::Time& value) { + first_paint_time_ = value; + } + + // The time that painting first happened after the document finished loading. + const base::Time& first_paint_after_load_time() const { + return first_paint_after_load_time_; + } + void set_first_paint_after_load_time(const base::Time& value) { + first_paint_after_load_time_ = value; + } + + // The time that a prerendered page was displayed. Invalid for + // non-prerendered pages. Can be either before or after + // |finish_document_load_time_|. + const base::Time& prerendered_page_display_time() const; + void set_prerendered_page_display_time(const base::Time& value); + + // True iff the histograms for the associated frame have been dumped. + bool load_histograms_recorded() const { return load_histograms_recorded_; } + void set_load_histograms_recorded(bool value) { + load_histograms_recorded_ = value; + } + + bool web_timing_histograms_recorded() const { + return web_timing_histograms_recorded_; + } + void set_web_timing_histograms_recorded(bool value) { + web_timing_histograms_recorded_ = value; + } + + // True if we have already processed the "DidCommitLoad" event for this + // request. Used by session history. + bool request_committed() const { return request_committed_; } + void set_request_committed(bool value) { request_committed_ = value; } + + // True if this navigation was not initiated via WebFrame::LoadRequest. + bool is_content_initiated() const { return is_content_initiated_; } + + const GURL& searchable_form_url() const { return searchable_form_url_; } + void set_searchable_form_url(const GURL& url) { searchable_form_url_ = url; } + const std::string& searchable_form_encoding() const { + return searchable_form_encoding_; + } + void set_searchable_form_encoding(const std::string& encoding) { + searchable_form_encoding_ = encoding; + } + + webkit_glue::PasswordForm* password_form_data() const { + return password_form_data_.get(); + } + void set_password_form_data(webkit_glue::PasswordForm* data); + + webkit_glue::AltErrorPageResourceFetcher* alt_error_page_fetcher() const { + return alt_error_page_fetcher_.get(); + } + void set_alt_error_page_fetcher(webkit_glue::AltErrorPageResourceFetcher* f); + + const std::string& security_info() const { return security_info_; } + void set_security_info(const std::string& security_info) { + security_info_ = security_info; + } + + bool use_error_page() const { return use_error_page_; } + void set_use_error_page(bool use_error_page) { + use_error_page_ = use_error_page; + } + + bool was_started_as_prerender() const; + void set_was_started_as_prerender(bool was_started_as_prerender); + + int http_status_code() const { return http_status_code_; } + void set_http_status_code(int http_status_code) { + http_status_code_ = http_status_code; + } + + // Sets the cache policy. The cache policy is only used if explicitly set and + // by default is not set. You can mark a NavigationState as not having a cache + // state by way of clear_cache_policy_override. + void set_cache_policy_override( + WebKit::WebURLRequest::CachePolicy cache_policy) { + cache_policy_override_ = cache_policy; + cache_policy_override_set_ = true; + } + WebKit::WebURLRequest::CachePolicy cache_policy_override() const { + return cache_policy_override_; + } + void clear_cache_policy_override() { + cache_policy_override_set_ = false; + cache_policy_override_ = WebKit::WebURLRequest::UseProtocolCachePolicy; + } + bool is_cache_policy_override_set() const { + return cache_policy_override_set_; + } + + // Indicator if SPDY was used as part of this page load. + void set_was_fetched_via_spdy(bool value) { was_fetched_via_spdy_ = value; } + bool was_fetched_via_spdy() const { return was_fetched_via_spdy_; } + + void set_was_npn_negotiated(bool value) { was_npn_negotiated_ = value; } + bool was_npn_negotiated() const { return was_npn_negotiated_; } + + void set_was_alternate_protocol_available(bool value) { + was_alternate_protocol_available_ = value; + } + bool was_alternate_protocol_available() const { + return was_alternate_protocol_available_; + } + + void set_was_fetched_via_proxy(bool value) { + was_fetched_via_proxy_ = value; + } + bool was_fetched_via_proxy() const { return was_fetched_via_proxy_; } + + // Whether the frame text contents was translated to a different language. + void set_was_translated(bool value) { was_translated_ = value; } + bool was_translated() const { return was_translated_; } + + // True iff the frame's navigation was within the same page. + void set_was_within_same_page(bool value) { was_within_same_page_ = value; } + bool was_within_same_page() const { return was_within_same_page_; } + + void set_was_prefetcher(bool value) { was_prefetcher_ = value; } + bool was_prefetcher() const { return was_prefetcher_; } + + void set_was_referred_by_prefetcher(bool value) { + was_referred_by_prefetcher_ = value; + } + bool was_referred_by_prefetcher() const { + return was_referred_by_prefetcher_; + } + + private: + NavigationState(PageTransition::Type transition_type, + const base::Time& request_time, + bool is_content_initiated, + int32 pending_page_id, + int pending_history_list_offset); + + PageTransition::Type transition_type_; + LoadType load_type_; + base::Time request_time_; + base::Time start_load_time_; + base::Time commit_load_time_; + base::Time finish_document_load_time_; + base::Time finish_load_time_; + base::Time first_paint_time_; + base::Time first_paint_after_load_time_; + base::Time prerendered_page_display_time_; + bool load_histograms_recorded_; + bool web_timing_histograms_recorded_; + bool request_committed_; + bool is_content_initiated_; + int32 pending_page_id_; + int pending_history_list_offset_; + GURL searchable_form_url_; + std::string searchable_form_encoding_; + scoped_ptr<webkit_glue::PasswordForm> password_form_data_; + scoped_ptr<webkit_glue::AltErrorPageResourceFetcher> alt_error_page_fetcher_; + std::string security_info_; + + // True if we should use an error page, if the http status code alos indicates + // an error. + bool use_error_page_; + + // True if a page load started as a prerender. Preserved across redirects. + bool was_started_as_prerender_; + + bool cache_policy_override_set_; + WebKit::WebURLRequest::CachePolicy cache_policy_override_; + + scoped_ptr<UserScriptIdleScheduler> user_script_idle_scheduler_; + int http_status_code_; + + bool was_fetched_via_spdy_; + bool was_npn_negotiated_; + bool was_alternate_protocol_available_; + bool was_fetched_via_proxy_; + bool was_translated_; + bool was_within_same_page_; + + // A prefetcher is a page that contains link rel=prefetch elements. + bool was_prefetcher_; + bool was_referred_by_prefetcher_; + + DISALLOW_COPY_AND_ASSIGN(NavigationState); +}; + +#endif // CHROME_RENDERER_NAVIGATION_STATE_H_ diff --git a/content/renderer/paint_aggregator.cc b/content/renderer/paint_aggregator.cc new file mode 100644 index 0000000..927630b --- /dev/null +++ b/content/renderer/paint_aggregator.cc @@ -0,0 +1,284 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/paint_aggregator.h" + +#include "base/logging.h" +#include "base/metrics/histogram.h" + +// ---------------------------------------------------------------------------- +// ALGORITHM NOTES +// +// We attempt to maintain a scroll rect in the presence of invalidations that +// are contained within the scroll rect. If an invalidation crosses a scroll +// rect, then we just treat the scroll rect as an invalidation rect. +// +// For invalidations performed prior to scrolling and contained within the +// scroll rect, we offset the invalidation rects to account for the fact that +// the consumer will perform scrolling before painting. +// +// We only support scrolling along one axis at a time. A diagonal scroll will +// therefore be treated as an invalidation. +// ---------------------------------------------------------------------------- + +// If the combined area of paint rects contained within the scroll rect grows +// too large, then we might as well just treat the scroll rect as a paint rect. +// This constant sets the max ratio of paint rect area to scroll rect area that +// we will tolerate before dograding the scroll into a repaint. +static const float kMaxRedundantPaintToScrollArea = 0.8f; + +// The maximum number of paint rects. If we exceed this limit, then we'll +// start combining paint rects (see CombinePaintRects). This limiting is +// important since the WebKit code associated with deciding what to paint given +// a paint rect can be significant. +static const size_t kMaxPaintRects = 5; + +// If the combined area of paint rects divided by the area of the union of all +// paint rects exceeds this threshold, then we will combine the paint rects. +static const float kMaxPaintRectsAreaRatio = 0.7f; + +PaintAggregator::PendingUpdate::PendingUpdate() {} + +PaintAggregator::PendingUpdate::~PendingUpdate() {} + +gfx::Rect PaintAggregator::PendingUpdate::GetScrollDamage() const { + // Should only be scrolling in one direction at a time. + DCHECK(!(scroll_delta.x() && scroll_delta.y())); + + gfx::Rect damaged_rect; + + // Compute the region we will expose by scrolling, and paint that into a + // shared memory section. + if (scroll_delta.x()) { + int dx = scroll_delta.x(); + damaged_rect.set_y(scroll_rect.y()); + damaged_rect.set_height(scroll_rect.height()); + if (dx > 0) { + damaged_rect.set_x(scroll_rect.x()); + damaged_rect.set_width(dx); + } else { + damaged_rect.set_x(scroll_rect.right() + dx); + damaged_rect.set_width(-dx); + } + } else { + int dy = scroll_delta.y(); + damaged_rect.set_x(scroll_rect.x()); + damaged_rect.set_width(scroll_rect.width()); + if (dy > 0) { + damaged_rect.set_y(scroll_rect.y()); + damaged_rect.set_height(dy); + } else { + damaged_rect.set_y(scroll_rect.bottom() + dy); + damaged_rect.set_height(-dy); + } + } + + // In case the scroll offset exceeds the width/height of the scroll rect + return scroll_rect.Intersect(damaged_rect); +} + +gfx::Rect PaintAggregator::PendingUpdate::GetPaintBounds() const { + gfx::Rect bounds; + for (size_t i = 0; i < paint_rects.size(); ++i) + bounds = bounds.Union(paint_rects[i]); + return bounds; +} + +bool PaintAggregator::HasPendingUpdate() const { + return !update_.scroll_rect.IsEmpty() || !update_.paint_rects.empty(); +} + +void PaintAggregator::ClearPendingUpdate() { + update_ = PendingUpdate(); +} + +void PaintAggregator::PopPendingUpdate(PendingUpdate* update) { + // Combine paint rects if their combined area is not sufficiently less than + // the area of the union of all paint rects. We skip this if there is a + // scroll rect since scrolling benefits from smaller paint rects. + if (update_.scroll_rect.IsEmpty() && update_.paint_rects.size() > 1) { + int paint_area = 0; + gfx::Rect union_rect; + for (size_t i = 0; i < update_.paint_rects.size(); ++i) { + paint_area += update_.paint_rects[i].size().GetArea(); + union_rect = union_rect.Union(update_.paint_rects[i]); + } + int union_area = union_rect.size().GetArea(); + if (float(paint_area) / float(union_area) > kMaxPaintRectsAreaRatio) + CombinePaintRects(); + } + *update = update_; + ClearPendingUpdate(); +} + +void PaintAggregator::InvalidateRect(const gfx::Rect& rect) { + // Combine overlapping paints using smallest bounding box. + for (size_t i = 0; i < update_.paint_rects.size(); ++i) { + const gfx::Rect& existing_rect = update_.paint_rects[i]; + if (existing_rect.Contains(rect)) // Optimize out redundancy. + return; + if (rect.Intersects(existing_rect) || rect.SharesEdgeWith(existing_rect)) { + // Re-invalidate in case the union intersects other paint rects. + gfx::Rect combined_rect = existing_rect.Union(rect); + update_.paint_rects.erase(update_.paint_rects.begin() + i); + InvalidateRect(combined_rect); + return; + } + } + + // Add a non-overlapping paint. + update_.paint_rects.push_back(rect); + + // If the new paint overlaps with a scroll, then it forces an invalidation of + // the scroll. If the new paint is contained by a scroll, then trim off the + // scroll damage to avoid redundant painting. + if (!update_.scroll_rect.IsEmpty()) { + if (ShouldInvalidateScrollRect(rect)) { + InvalidateScrollRect(); + } else if (update_.scroll_rect.Contains(rect)) { + update_.paint_rects[update_.paint_rects.size() - 1] = + rect.Subtract(update_.GetScrollDamage()); + if (update_.paint_rects[update_.paint_rects.size() - 1].IsEmpty()) + update_.paint_rects.erase(update_.paint_rects.end() - 1); + } + } + + if (update_.paint_rects.size() > kMaxPaintRects) + CombinePaintRects(); + + // Track how large the paint_rects vector grows during an invalidation + // sequence. Note: A subsequent invalidation may end up being combined + // with all existing paints, which means that tracking the size of + // paint_rects at the time when PopPendingUpdate() is called may mask + // certain performance problems. + HISTOGRAM_COUNTS_100("MPArch.RW_IntermediatePaintRectCount", + update_.paint_rects.size()); +} + +void PaintAggregator::ScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { + // We only support scrolling along one axis at a time. + if (dx != 0 && dy != 0) { + InvalidateRect(clip_rect); + return; + } + + // We can only scroll one rect at a time. + if (!update_.scroll_rect.IsEmpty() && + !update_.scroll_rect.Equals(clip_rect)) { + InvalidateRect(clip_rect); + return; + } + + // Again, we only support scrolling along one axis at a time. Make sure this + // update doesn't scroll on a different axis than any existing one. + if ((dx && update_.scroll_delta.y()) || (dy && update_.scroll_delta.x())) { + InvalidateRect(clip_rect); + return; + } + + // The scroll rect is new or isn't changing (though the scroll amount may + // be changing). + update_.scroll_rect = clip_rect; + update_.scroll_delta.Offset(dx, dy); + + // We might have just wiped out a pre-existing scroll. + if (update_.scroll_delta == gfx::Point()) { + update_.scroll_rect = gfx::Rect(); + return; + } + + // Adjust any contained paint rects and check for any overlapping paints. + for (size_t i = 0; i < update_.paint_rects.size(); ++i) { + if (update_.scroll_rect.Contains(update_.paint_rects[i])) { + update_.paint_rects[i] = ScrollPaintRect(update_.paint_rects[i], dx, dy); + // The rect may have been scrolled out of view. + if (update_.paint_rects[i].IsEmpty()) { + update_.paint_rects.erase(update_.paint_rects.begin() + i); + i--; + } + } else if (update_.scroll_rect.Intersects(update_.paint_rects[i])) { + InvalidateScrollRect(); + return; + } + } + + // If the new scroll overlaps too much with contained paint rects, then force + // an invalidation of the scroll. + if (ShouldInvalidateScrollRect(gfx::Rect())) + InvalidateScrollRect(); +} + +gfx::Rect PaintAggregator::ScrollPaintRect(const gfx::Rect& paint_rect, + int dx, int dy) const { + gfx::Rect result = paint_rect; + + result.Offset(dx, dy); + result = update_.scroll_rect.Intersect(result); + + // Subtract out the scroll damage rect to avoid redundant painting. + return result.Subtract(update_.GetScrollDamage()); +} + +bool PaintAggregator::ShouldInvalidateScrollRect(const gfx::Rect& rect) const { + if (!rect.IsEmpty()) { + if (!update_.scroll_rect.Intersects(rect)) + return false; + + if (!update_.scroll_rect.Contains(rect)) + return true; + } + + // Check if the combined area of all contained paint rects plus this new + // rect comes too close to the area of the scroll_rect. If so, then we + // might as well invalidate the scroll rect. + + int paint_area = rect.size().GetArea(); + for (size_t i = 0; i < update_.paint_rects.size(); ++i) { + const gfx::Rect& existing_rect = update_.paint_rects[i]; + if (update_.scroll_rect.Contains(existing_rect)) + paint_area += existing_rect.size().GetArea(); + } + int scroll_area = update_.scroll_rect.size().GetArea(); + if (float(paint_area) / float(scroll_area) > kMaxRedundantPaintToScrollArea) + return true; + + return false; +} + +void PaintAggregator::InvalidateScrollRect() { + gfx::Rect scroll_rect = update_.scroll_rect; + update_.scroll_rect = gfx::Rect(); + update_.scroll_delta = gfx::Point(); + InvalidateRect(scroll_rect); +} + +void PaintAggregator::CombinePaintRects() { + // Combine paint rects do to at most two rects: one inside the scroll_rect + // and one outside the scroll_rect. If there is no scroll_rect, then just + // use the smallest bounding box for all paint rects. + // + // NOTE: This is a fairly simple algorithm. We could get fancier by only + // combining two rects to get us under the kMaxPaintRects limit, but if we + // reach this method then it means we're hitting a rare case, so there's no + // need to over-optimize it. + // + if (update_.scroll_rect.IsEmpty()) { + gfx::Rect bounds = update_.GetPaintBounds(); + update_.paint_rects.clear(); + update_.paint_rects.push_back(bounds); + } else { + gfx::Rect inner, outer; + for (size_t i = 0; i < update_.paint_rects.size(); ++i) { + const gfx::Rect& existing_rect = update_.paint_rects[i]; + if (update_.scroll_rect.Contains(existing_rect)) { + inner = inner.Union(existing_rect); + } else { + outer = outer.Union(existing_rect); + } + } + update_.paint_rects.clear(); + update_.paint_rects.push_back(inner); + update_.paint_rects.push_back(outer); + } +} diff --git a/content/renderer/paint_aggregator.h b/content/renderer/paint_aggregator.h new file mode 100644 index 0000000..84d36907 --- /dev/null +++ b/content/renderer/paint_aggregator.h @@ -0,0 +1,64 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_PAINT_AGGREGATOR_H_ +#define CONTENT_RENDERER_PAINT_AGGREGATOR_H_ +#pragma once + +#include <vector> + +#include "base/basictypes.h" +#include "ui/gfx/rect.h" + +// This class is responsible for aggregating multiple invalidation and scroll +// commands to produce a scroll and repaint sequence. +class PaintAggregator { + public: + // This structure describes an aggregation of InvalidateRect and ScrollRect + // calls. If |scroll_rect| is non-empty, then that rect should be scrolled + // by the amount specified by |scroll_delta|. If |paint_rects| is non-empty, + // then those rects should be repainted. If |scroll_rect| and |paint_rects| + // are non-empty, then scrolling should be performed before repainting. + // |scroll_delta| can only specify scrolling in one direction (i.e., the x + // and y members cannot both be non-zero). + struct PendingUpdate { + PendingUpdate(); + ~PendingUpdate(); + + // Returns the rect damaged by scrolling within |scroll_rect| by + // |scroll_delta|. This rect must be repainted. + gfx::Rect GetScrollDamage() const; + + // Returns the smallest rect containing all paint rects. + gfx::Rect GetPaintBounds() const; + + gfx::Point scroll_delta; + gfx::Rect scroll_rect; + std::vector<gfx::Rect> paint_rects; + }; + + // There is a PendingUpdate if InvalidateRect or ScrollRect were called and + // ClearPendingUpdate was not called. + bool HasPendingUpdate() const; + void ClearPendingUpdate(); + + // Fills |update| and clears the pending update. + void PopPendingUpdate(PendingUpdate* update); + + // The given rect should be repainted. + void InvalidateRect(const gfx::Rect& rect); + + // The given rect should be scrolled by the given amounts. + void ScrollRect(int dx, int dy, const gfx::Rect& clip_rect); + + private: + gfx::Rect ScrollPaintRect(const gfx::Rect& paint_rect, int dx, int dy) const; + bool ShouldInvalidateScrollRect(const gfx::Rect& rect) const; + void InvalidateScrollRect(); + void CombinePaintRects(); + + PendingUpdate update_; +}; + +#endif // CONTENT_RENDERER_PAINT_AGGREGATOR_H_ diff --git a/content/renderer/paint_aggregator_unittest.cc b/content/renderer/paint_aggregator_unittest.cc new file mode 100644 index 0000000..1874270 --- /dev/null +++ b/content/renderer/paint_aggregator_unittest.cc @@ -0,0 +1,437 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/paint_aggregator.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(PaintAggregator, InitialState) { + PaintAggregator greg; + EXPECT_FALSE(greg.HasPendingUpdate()); +} + +TEST(PaintAggregator, SingleInvalidation) { + PaintAggregator greg; + + gfx::Rect rect(2, 4, 10, 16); + greg.InvalidateRect(rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + ASSERT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, DoubleDisjointInvalidation) { + PaintAggregator greg; + + gfx::Rect r1(2, 4, 2, 40); + gfx::Rect r2(4, 2, 40, 2); + + greg.InvalidateRect(r1); + greg.InvalidateRect(r2); + + gfx::Rect expected_bounds = r1.Union(r2); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(2U, update.paint_rects.size()); + + EXPECT_EQ(expected_bounds, update.GetPaintBounds()); +} + +TEST(PaintAggregator, DisjointInvalidationsCombined) { + PaintAggregator greg; + + // Make the rectangles such that they don't overlap but cover a very large + // percentage of the area of covered by their union. This is so we're not + // very sensitive to the combining heuristic in the paint aggregator. + gfx::Rect r1(2, 4, 2, 1000); + gfx::Rect r2(5, 2, 2, 1000); + + greg.InvalidateRect(r1); + greg.InvalidateRect(r2); + + gfx::Rect expected_bounds = r1.Union(r2); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + ASSERT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(expected_bounds, update.paint_rects[0]); +} + +TEST(PaintAggregator, SingleScroll) { + PaintAggregator greg; + + gfx::Rect rect(1, 2, 3, 4); + gfx::Point delta(1, 0); + greg.ScrollRect(delta.x(), delta.y(), rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.paint_rects.empty()); + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + + EXPECT_EQ(rect, update.scroll_rect); + + EXPECT_EQ(delta.x(), update.scroll_delta.x()); + EXPECT_EQ(delta.y(), update.scroll_delta.y()); + + gfx::Rect resulting_damage = update.GetScrollDamage(); + gfx::Rect expected_damage(1, 2, 1, 4); + EXPECT_EQ(expected_damage, resulting_damage); +} + +TEST(PaintAggregator, DoubleOverlappingScroll) { + PaintAggregator greg; + + gfx::Rect rect(1, 2, 3, 4); + gfx::Point delta1(1, 0); + gfx::Point delta2(1, 0); + greg.ScrollRect(delta1.x(), delta1.y(), rect); + greg.ScrollRect(delta2.x(), delta2.y(), rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.paint_rects.empty()); + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + + EXPECT_EQ(rect, update.scroll_rect); + + gfx::Point expected_delta(delta1.x() + delta2.x(), + delta1.y() + delta2.y()); + EXPECT_EQ(expected_delta.x(), update.scroll_delta.x()); + EXPECT_EQ(expected_delta.y(), update.scroll_delta.y()); + + gfx::Rect resulting_damage = update.GetScrollDamage(); + gfx::Rect expected_damage(1, 2, 2, 4); + EXPECT_EQ(expected_damage, resulting_damage); +} + +TEST(PaintAggregator, NegatingScroll) { + PaintAggregator greg; + + // Scroll twice in opposite directions by equal amounts. The result + // should be no scrolling. + + gfx::Rect rect(1, 2, 3, 4); + gfx::Point delta1(1, 0); + gfx::Point delta2(-1, 0); + greg.ScrollRect(delta1.x(), delta1.y(), rect); + greg.ScrollRect(delta2.x(), delta2.y(), rect); + + EXPECT_FALSE(greg.HasPendingUpdate()); +} + +TEST(PaintAggregator, DiagonalScroll) { + PaintAggregator greg; + + // We don't support optimized diagonal scrolling, so this should result in + // repainting. + + gfx::Rect rect(1, 2, 3, 4); + gfx::Point delta(1, 1); + greg.ScrollRect(delta.x(), delta.y(), rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + ASSERT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, ContainedPaintAfterScroll) { + PaintAggregator greg; + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(2, 0, scroll_rect); + + gfx::Rect paint_rect(4, 4, 2, 2); + greg.InvalidateRect(paint_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + // expecting a paint rect inside the scroll rect + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(scroll_rect, update.scroll_rect); + EXPECT_EQ(paint_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, ContainedPaintBeforeScroll) { + PaintAggregator greg; + + gfx::Rect paint_rect(4, 4, 2, 2); + greg.InvalidateRect(paint_rect); + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(2, 0, scroll_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + // Expecting a paint rect inside the scroll rect + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + paint_rect.Offset(2, 0); + + EXPECT_EQ(scroll_rect, update.scroll_rect); + EXPECT_EQ(paint_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, ContainedPaintsBeforeAndAfterScroll) { + PaintAggregator greg; + + gfx::Rect paint_rect1(4, 4, 2, 2); + greg.InvalidateRect(paint_rect1); + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(2, 0, scroll_rect); + + gfx::Rect paint_rect2(6, 4, 2, 2); + greg.InvalidateRect(paint_rect2); + + gfx::Rect expected_paint_rect = paint_rect2; + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + // Expecting a paint rect inside the scroll rect + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(scroll_rect, update.scroll_rect); + EXPECT_EQ(expected_paint_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, LargeContainedPaintAfterScroll) { + PaintAggregator greg; + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(0, 1, scroll_rect); + + gfx::Rect paint_rect(0, 0, 10, 9); // Repaint 90% + greg.InvalidateRect(paint_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(scroll_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, LargeContainedPaintBeforeScroll) { + PaintAggregator greg; + + gfx::Rect paint_rect(0, 0, 10, 9); // Repaint 90% + greg.InvalidateRect(paint_rect); + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(0, 1, scroll_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(scroll_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, OverlappingPaintBeforeScroll) { + PaintAggregator greg; + + gfx::Rect paint_rect(4, 4, 10, 2); + greg.InvalidateRect(paint_rect); + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(2, 0, scroll_rect); + + gfx::Rect expected_paint_rect = scroll_rect.Union(paint_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(expected_paint_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, OverlappingPaintAfterScroll) { + PaintAggregator greg; + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(2, 0, scroll_rect); + + gfx::Rect paint_rect(4, 4, 10, 2); + greg.InvalidateRect(paint_rect); + + gfx::Rect expected_paint_rect = scroll_rect.Union(paint_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_TRUE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(expected_paint_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, DisjointPaintBeforeScroll) { + PaintAggregator greg; + + gfx::Rect paint_rect(4, 4, 10, 2); + greg.InvalidateRect(paint_rect); + + gfx::Rect scroll_rect(0, 0, 2, 10); + greg.ScrollRect(2, 0, scroll_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(paint_rect, update.paint_rects[0]); + EXPECT_EQ(scroll_rect, update.scroll_rect); +} + +TEST(PaintAggregator, DisjointPaintAfterScroll) { + PaintAggregator greg; + + gfx::Rect scroll_rect(0, 0, 2, 10); + greg.ScrollRect(2, 0, scroll_rect); + + gfx::Rect paint_rect(4, 4, 10, 2); + greg.InvalidateRect(paint_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(paint_rect, update.paint_rects[0]); + EXPECT_EQ(scroll_rect, update.scroll_rect); +} + +TEST(PaintAggregator, ContainedPaintTrimmedByScroll) { + PaintAggregator greg; + + gfx::Rect paint_rect(4, 4, 6, 6); + greg.InvalidateRect(paint_rect); + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(2, 0, scroll_rect); + + // The paint rect should have become narrower. + gfx::Rect expected_paint_rect(6, 4, 4, 6); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(expected_paint_rect, update.paint_rects[0]); + EXPECT_EQ(scroll_rect, update.scroll_rect); +} + +TEST(PaintAggregator, ContainedPaintEliminatedByScroll) { + PaintAggregator greg; + + gfx::Rect paint_rect(4, 4, 6, 6); + greg.InvalidateRect(paint_rect); + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(6, 0, scroll_rect); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_TRUE(update.paint_rects.empty()); + + EXPECT_EQ(scroll_rect, update.scroll_rect); +} + +TEST(PaintAggregator, ContainedPaintAfterScrollTrimmedByScrollDamage) { + PaintAggregator greg; + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(4, 0, scroll_rect); + + gfx::Rect paint_rect(2, 0, 4, 10); + greg.InvalidateRect(paint_rect); + + gfx::Rect expected_scroll_damage(0, 0, 4, 10); + gfx::Rect expected_paint_rect(4, 0, 2, 10); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_EQ(1U, update.paint_rects.size()); + + EXPECT_EQ(scroll_rect, update.scroll_rect); + EXPECT_EQ(expected_scroll_damage, update.GetScrollDamage()); + EXPECT_EQ(expected_paint_rect, update.paint_rects[0]); +} + +TEST(PaintAggregator, ContainedPaintAfterScrollEliminatedByScrollDamage) { + PaintAggregator greg; + + gfx::Rect scroll_rect(0, 0, 10, 10); + greg.ScrollRect(4, 0, scroll_rect); + + gfx::Rect paint_rect(2, 0, 2, 10); + greg.InvalidateRect(paint_rect); + + gfx::Rect expected_scroll_damage(0, 0, 4, 10); + + EXPECT_TRUE(greg.HasPendingUpdate()); + PaintAggregator::PendingUpdate update; + greg.PopPendingUpdate(&update); + + EXPECT_FALSE(update.scroll_rect.IsEmpty()); + EXPECT_TRUE(update.paint_rects.empty()); + + EXPECT_EQ(scroll_rect, update.scroll_rect); + EXPECT_EQ(expected_scroll_damage, update.GetScrollDamage()); +} diff --git a/content/renderer/pepper_platform_context_3d_impl.cc b/content/renderer/pepper_platform_context_3d_impl.cc new file mode 100644 index 0000000..a7c396f --- /dev/null +++ b/content/renderer/pepper_platform_context_3d_impl.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/pepper_platform_context_3d_impl.h" + +#include "chrome/renderer/render_thread.h" +#include "content/renderer/command_buffer_proxy.h" +#include "content/renderer/ggl.h" +#include "content/renderer/gpu_channel_host.h" +#include "gpu/command_buffer/client/gles2_cmd_helper.h" +#include "gpu/command_buffer/client/gles2_implementation.h" + +#ifdef ENABLE_GPU +PlatformContext3DImpl::PlatformContext3DImpl(ggl::Context* parent_context) + : parent_context_(ggl::GetWeakContextReference(parent_context)), + parent_texture_id_(0), + command_buffer_(NULL), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +PlatformContext3DImpl::~PlatformContext3DImpl() { + if (command_buffer_) { + DCHECK(channel_.get()); + channel_->DestroyCommandBuffer(command_buffer_); + command_buffer_ = NULL; + } + + channel_ = NULL; + + if (parent_context_.get() && parent_texture_id_ != 0) { + ggl::GetImplementation(parent_context_)->FreeTextureId(parent_texture_id_); + } + +} + +bool PlatformContext3DImpl::Init() { + // Ignore initializing more than once. + if (command_buffer_) + return true; + + // Parent may already have been deleted. + if (!parent_context_.get()) + return false; + + RenderThread* render_thread = RenderThread::current(); + if (!render_thread) + return false; + + channel_ = render_thread->GetGpuChannel(); + if (!channel_.get()) + return false; + + DCHECK(channel_->state() == GpuChannelHost::kConnected); + + // Flush any remaining commands in the parent context to make sure the + // texture id accounting stays consistent. + gpu::gles2::GLES2Implementation* parent_gles2 = + ggl::GetImplementation(parent_context_); + parent_gles2->helper()->CommandBufferHelper::Finish(); + parent_texture_id_ = parent_gles2->MakeTextureId(); + + // TODO(apatrick): Let Pepper plugins configure their back buffer surface. + static const int32 kAttribs[] = { + ggl::GGL_ALPHA_SIZE, 8, + ggl::GGL_DEPTH_SIZE, 24, + ggl::GGL_STENCIL_SIZE, 8, + ggl::GGL_SAMPLES, 0, + ggl::GGL_SAMPLE_BUFFERS, 0, + ggl::GGL_NONE, + }; + std::vector<int32> attribs(kAttribs, kAttribs + ARRAYSIZE_UNSAFE(kAttribs)); + CommandBufferProxy* parent_command_buffer = + ggl::GetCommandBufferProxy(parent_context_); + command_buffer_ = channel_->CreateOffscreenCommandBuffer( + parent_command_buffer, + gfx::Size(1, 1), + "*", + attribs, + parent_texture_id_); + if (!command_buffer_) + return false; + command_buffer_->SetChannelErrorCallback(callback_factory_.NewCallback( + &PlatformContext3DImpl::OnContextLost)); + + return true; +} + +void PlatformContext3DImpl::SetSwapBuffersCallback(Callback0::Type* callback) { + DCHECK(command_buffer_); + command_buffer_->SetSwapBuffersCallback(callback); +} + +unsigned PlatformContext3DImpl::GetBackingTextureId() { + DCHECK(command_buffer_); + return parent_texture_id_; +} + +gpu::CommandBuffer* PlatformContext3DImpl::GetCommandBuffer() { + return command_buffer_; +} + +void PlatformContext3DImpl::SetContextLostCallback(Callback0::Type* callback) { + context_lost_callback_.reset(callback); +} + +void PlatformContext3DImpl::OnContextLost() { + DCHECK(command_buffer_); + + if (context_lost_callback_.get()) + context_lost_callback_->Run(); +} + +#endif // ENABLE_GPU diff --git a/content/renderer/pepper_platform_context_3d_impl.h b/content/renderer/pepper_platform_context_3d_impl.h new file mode 100644 index 0000000..4e8b7bf --- /dev/null +++ b/content/renderer/pepper_platform_context_3d_impl.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef CONTENT_RENDERER_PEPPER_PLATFORM_CONTEXT_3D_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PLATFORM_CONTEXT_3D_IMPL_H_ + +#include "base/callback.h" +#include "base/scoped_callback_factory.h" +#include "base/scoped_ptr.h" +#include "base/weak_ptr.h" +#include "webkit/plugins/ppapi/plugin_delegate.h" + +#ifdef ENABLE_GPU + +namespace gpu { + +class CommandBuffer; + +} // namespace gpu + +namespace ggl { + +class Context; + +} // namespace ggl; + +class GpuChannelHost; +class CommandBufferProxy; + +class PlatformContext3DImpl + : public webkit::ppapi::PluginDelegate::PlatformContext3D { + public: + explicit PlatformContext3DImpl(ggl::Context* parent_context); + virtual ~PlatformContext3DImpl(); + + virtual bool Init(); + virtual void SetSwapBuffersCallback(Callback0::Type* callback); + virtual unsigned GetBackingTextureId(); + virtual gpu::CommandBuffer* GetCommandBuffer(); + virtual void SetContextLostCallback(Callback0::Type* callback); + + private: + bool InitRaw(); + void OnContextLost(); + + base::WeakPtr<ggl::Context> parent_context_; + scoped_refptr<GpuChannelHost> channel_; + unsigned int parent_texture_id_; + CommandBufferProxy* command_buffer_; + scoped_ptr<Callback0::Type> context_lost_callback_; + base::ScopedCallbackFactory<PlatformContext3DImpl> callback_factory_; +}; + +#endif // ENABLE_GPU + +#endif // CONTENT_RENDERER_PEPPER_PLATFORM_CONTEXT_3D_IMPL_H_ diff --git a/content/renderer/pepper_plugin_delegate_impl.cc b/content/renderer/pepper_plugin_delegate_impl.cc new file mode 100644 index 0000000..777de0b --- /dev/null +++ b/content/renderer/pepper_plugin_delegate_impl.cc @@ -0,0 +1,958 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/pepper_plugin_delegate_impl.h" + +#include <cmath> +#include <queue> + +#include "app/surface/transport_dib.h" +#include "base/callback.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_split.h" +#include "base/task.h" +#include "base/time.h" +#include "chrome/common/pepper_plugin_registry.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/render_messages_params.h" +#include "chrome/renderer/render_thread.h" +#include "chrome/renderer/render_view.h" +#include "content/common/child_process_messages.h" +#include "content/common/child_thread.h" +#include "content/common/file_system/file_system_dispatcher.h" +#include "content/common/pepper_file_messages.h" +#include "content/common/pepper_messages.h" +#include "content/renderer/audio_message_filter.h" +#include "content/renderer/command_buffer_proxy.h" +#include "content/renderer/content_renderer_client.h" +#include "content/renderer/ggl.h" +#include "content/renderer/gpu_channel_host.h" +#include "content/renderer/pepper_platform_context_3d_impl.h" +#include "content/renderer/webgraphicscontext3d_command_buffer_impl.h" +#include "content/renderer/webplugin_delegate_proxy.h" +#include "ipc/ipc_channel_handle.h" +#include "ppapi/c/dev/pp_video_dev.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/private/ppb_flash.h" +#include "ppapi/c/private/ppb_flash_net_connector.h" +#include "ppapi/proxy/host_dispatcher.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserCompletion.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserParams.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" +#include "ui/gfx/size.h" +#include "webkit/fileapi/file_system_callback_dispatcher.h" +#include "webkit/glue/context_menu.h" +#include "webkit/plugins/npapi/webplugin.h" +#include "webkit/plugins/ppapi/file_path.h" +#include "webkit/plugins/ppapi/ppb_file_io_impl.h" +#include "webkit/plugins/ppapi/plugin_module.h" +#include "webkit/plugins/ppapi/ppapi_plugin_instance.h" +#include "webkit/plugins/ppapi/ppb_flash_impl.h" +#include "webkit/plugins/ppapi/ppb_flash_net_connector_impl.h" + +using WebKit::WebView; + +namespace { + +const int32 kDefaultCommandBufferSize = 1024 * 1024; + +// Implements the Image2D using a TransportDIB. +class PlatformImage2DImpl + : public webkit::ppapi::PluginDelegate::PlatformImage2D { + public: + // This constructor will take ownership of the dib pointer. + // On Mac, we assume that the dib is cached by the browser, so on destruction + // we'll tell the browser to free it. + PlatformImage2DImpl(int width, int height, TransportDIB* dib) + : width_(width), + height_(height), + dib_(dib) { + } + +#if defined(OS_MACOSX) + // On Mac, we have to tell the browser to free the transport DIB. + virtual ~PlatformImage2DImpl() { + if (dib_.get()) { + RenderThread::current()->Send( + new ViewHostMsg_FreeTransportDIB(dib_->id())); + } + } +#endif + + virtual skia::PlatformCanvas* Map() { + return dib_->GetPlatformCanvas(width_, height_); + } + + virtual intptr_t GetSharedMemoryHandle(uint32* byte_count) const { + *byte_count = dib_->size(); +#if defined(OS_WIN) + return reinterpret_cast<intptr_t>(dib_->handle()); +#elif defined(OS_MACOSX) + return static_cast<intptr_t>(dib_->handle().fd); +#elif defined(OS_LINUX) + return static_cast<intptr_t>(dib_->handle()); +#endif + } + + virtual TransportDIB* GetTransportDIB() const { + return dib_.get(); + } + + private: + int width_; + int height_; + scoped_ptr<TransportDIB> dib_; + + DISALLOW_COPY_AND_ASSIGN(PlatformImage2DImpl); +}; + + +class PlatformAudioImpl + : public webkit::ppapi::PluginDelegate::PlatformAudio, + public AudioMessageFilter::Delegate, + public base::RefCountedThreadSafe<PlatformAudioImpl> { + public: + explicit PlatformAudioImpl(scoped_refptr<AudioMessageFilter> filter) + : client_(NULL), filter_(filter), stream_id_(0), + main_message_loop_(MessageLoop::current()) { + DCHECK(filter_); + } + + virtual ~PlatformAudioImpl() { + // Make sure we have been shut down. Warning: this will usually happen on + // the I/O thread! + DCHECK_EQ(0, stream_id_); + DCHECK(!client_); + } + + // Initialize this audio context. StreamCreated() will be called when the + // stream is created. + bool Initialize(uint32_t sample_rate, uint32_t sample_count, + webkit::ppapi::PluginDelegate::PlatformAudio::Client* client); + + // PlatformAudio implementation (called on main thread). + virtual bool StartPlayback(); + virtual bool StopPlayback(); + virtual void ShutDown(); + + private: + // I/O thread backends to above functions. + void InitializeOnIOThread( + const ViewHostMsg_Audio_CreateStream_Params& params); + void StartPlaybackOnIOThread(); + void StopPlaybackOnIOThread(); + void ShutDownOnIOThread(); + + virtual void OnRequestPacket(AudioBuffersState buffers_state) { + LOG(FATAL) << "Should never get OnRequestPacket in PlatformAudioImpl"; + } + + virtual void OnStateChanged(const ViewMsg_AudioStreamState_Params& state) {} + + virtual void OnCreated(base::SharedMemoryHandle handle, uint32 length) { + LOG(FATAL) << "Should never get OnCreated in PlatformAudioImpl"; + } + + virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle, + base::SyncSocket::Handle socket_handle, + uint32 length); + + virtual void OnVolume(double volume) {} + + // The client to notify when the stream is created. THIS MUST ONLY BE + // ACCESSED ON THE MAIN THREAD. + webkit::ppapi::PluginDelegate::PlatformAudio::Client* client_; + + // MessageFilter used to send/receive IPC. THIS MUST ONLY BE ACCESSED ON THE + // I/O thread except to send messages and get the message loop. + scoped_refptr<AudioMessageFilter> filter_; + + // Our ID on the MessageFilter. THIS MUST ONLY BE ACCESSED ON THE I/O THREAD + // or else you could race with the initialize function which sets it. + int32 stream_id_; + + MessageLoop* main_message_loop_; + + DISALLOW_COPY_AND_ASSIGN(PlatformAudioImpl); +}; + +bool PlatformAudioImpl::Initialize( + uint32_t sample_rate, uint32_t sample_count, + webkit::ppapi::PluginDelegate::PlatformAudio::Client* client) { + + DCHECK(client); + // Make sure we don't call init more than once. + DCHECK_EQ(0, stream_id_); + + client_ = client; + + ViewHostMsg_Audio_CreateStream_Params params; + params.params.format = AudioParameters::AUDIO_PCM_LINEAR; + params.params.channels = 2; + params.params.sample_rate = sample_rate; + params.params.bits_per_sample = 16; + params.params.samples_per_packet = sample_count; + + filter_->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &PlatformAudioImpl::InitializeOnIOThread, + params)); + return true; +} + +bool PlatformAudioImpl::StartPlayback() { + if (filter_) { + filter_->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &PlatformAudioImpl::StartPlaybackOnIOThread)); + return true; + } + return false; +} + +bool PlatformAudioImpl::StopPlayback() { + if (filter_) { + filter_->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &PlatformAudioImpl::StopPlaybackOnIOThread)); + return true; + } + return false; +} + +void PlatformAudioImpl::ShutDown() { + // Called on the main thread to stop all audio callbacks. We must only change + // the client on the main thread, and the delegates from the I/O thread. + client_ = NULL; + filter_->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &PlatformAudioImpl::ShutDownOnIOThread)); +} + +void PlatformAudioImpl::InitializeOnIOThread( + const ViewHostMsg_Audio_CreateStream_Params& params) { + stream_id_ = filter_->AddDelegate(this); + filter_->Send(new ViewHostMsg_CreateAudioStream(0, stream_id_, params, true)); +} + +void PlatformAudioImpl::StartPlaybackOnIOThread() { + if (stream_id_) + filter_->Send(new ViewHostMsg_PlayAudioStream(0, stream_id_)); +} + +void PlatformAudioImpl::StopPlaybackOnIOThread() { + if (stream_id_) + filter_->Send(new ViewHostMsg_PauseAudioStream(0, stream_id_)); +} + +void PlatformAudioImpl::ShutDownOnIOThread() { + // Make sure we don't call shutdown more than once. + if (!stream_id_) + return; + + filter_->Send(new ViewHostMsg_CloseAudioStream(0, stream_id_)); + filter_->RemoveDelegate(stream_id_); + stream_id_ = 0; + + Release(); // Release for the delegate, balances out the reference taken in + // PepperPluginDelegateImpl::CreateAudio. +} + +void PlatformAudioImpl::OnLowLatencyCreated( + base::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle, + uint32 length) { +#if defined(OS_WIN) + DCHECK(handle); + DCHECK(socket_handle); +#else + DCHECK_NE(-1, handle.fd); + DCHECK_NE(-1, socket_handle); +#endif + DCHECK(length); + + if (MessageLoop::current() == main_message_loop_) { + // Must dereference the client only on the main thread. Shutdown may have + // occurred while the request was in-flight, so we need to NULL check. + if (client_) + client_->StreamCreated(handle, length, socket_handle); + } else { + main_message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PlatformAudioImpl::OnLowLatencyCreated, + handle, socket_handle, length)); + } +} + +// Implements the VideoDecoder. +class PlatformVideoDecoderImpl + : public webkit::ppapi::PluginDelegate::PlatformVideoDecoder { + public: + PlatformVideoDecoderImpl() + : input_buffer_size_(0), + next_dib_id_(0), + dib_(NULL) { + memset(&decoder_config_, 0, sizeof(decoder_config_)); + memset(&flush_callback_, 0, sizeof(flush_callback_)); + } + + virtual bool Init(const PP_VideoDecoderConfig_Dev& decoder_config) { + decoder_config_ = decoder_config; + input_buffer_size_ = 1024 << 4; + + // Allocate the transport DIB. + TransportDIB* dib = TransportDIB::Create(input_buffer_size_, + next_dib_id_++); + if (!dib) + return false; + + // TODO(wjia): Create video decoder in GPU process. + // Meanwhile, delete |dib| to free the resource. + delete dib; + + return true; + } + + virtual bool Decode(PP_VideoCompressedDataBuffer_Dev& input_buffer) { + // TODO(wjia): Implement me! + NOTIMPLEMENTED(); + + input_buffers_.push(&input_buffer); + + // Copy input data to dib_ and send it to GPU video decoder. + + return false; + } + + virtual int32_t Flush(PP_CompletionCallback& callback) { + // TODO(wjia): Implement me! + NOTIMPLEMENTED(); + + // Do nothing if there is a flush pending. + if (flush_callback_.func) + return PP_ERROR_BADARGUMENT; + + flush_callback_ = callback; + + // Call GPU video decoder to flush. + + return PP_ERROR_WOULDBLOCK; + } + + virtual bool ReturnUncompressedDataBuffer( + PP_VideoUncompressedDataBuffer_Dev& buffer) { + // TODO(wjia): Implement me! + NOTIMPLEMENTED(); + + // Deliver the buffer to GPU video decoder. + + return false; + } + + void OnFlushDone() { + if (!flush_callback_.func) + return; + + flush_callback_.func(flush_callback_.user_data, PP_OK); + flush_callback_.func = NULL; + } + + virtual intptr_t GetSharedMemoryHandle() const { + return reinterpret_cast<intptr_t>(dib_.get()); + } + + private: + size_t input_buffer_size_; + int next_dib_id_; + scoped_ptr<TransportDIB> dib_; + PP_VideoDecoderConfig_Dev decoder_config_; + std::queue<PP_VideoCompressedDataBuffer_Dev*> input_buffers_; + PP_CompletionCallback flush_callback_; + + DISALLOW_COPY_AND_ASSIGN(PlatformVideoDecoderImpl); +}; + +class DispatcherWrapper + : public webkit::ppapi::PluginDelegate::OutOfProcessProxy { + public: + DispatcherWrapper() {} + virtual ~DispatcherWrapper() {} + + bool Init(base::ProcessHandle plugin_process_handle, + IPC::ChannelHandle channel_handle, + PP_Module pp_module, + pp::proxy::Dispatcher::GetInterfaceFunc local_get_interface); + + // OutOfProcessProxy implementation. + virtual const void* GetProxiedInterface(const char* name) { + return dispatcher_->GetProxiedInterface(name); + } + virtual void AddInstance(PP_Instance instance) { + pp::proxy::HostDispatcher::SetForInstance(instance, dispatcher_.get()); + } + virtual void RemoveInstance(PP_Instance instance) { + pp::proxy::HostDispatcher::RemoveForInstance(instance); + } + + private: + scoped_ptr<pp::proxy::HostDispatcher> dispatcher_; +}; + +bool DispatcherWrapper::Init( + base::ProcessHandle plugin_process_handle, + IPC::ChannelHandle channel_handle, + PP_Module pp_module, + pp::proxy::Dispatcher::GetInterfaceFunc local_get_interface) { + dispatcher_.reset(new pp::proxy::HostDispatcher( + plugin_process_handle, pp_module, local_get_interface)); + + if (!dispatcher_->InitWithChannel(PepperPluginRegistry::GetInstance(), + channel_handle, true)) { + dispatcher_.reset(); + return false; + } + return true; +} + +} // namespace + +PepperPluginDelegateImpl::PepperPluginDelegateImpl(RenderView* render_view) + : render_view_(render_view), + has_saved_context_menu_action_(false), + saved_context_menu_action_(0), + id_generator_(0) { +} + +PepperPluginDelegateImpl::~PepperPluginDelegateImpl() { +} + +scoped_refptr<webkit::ppapi::PluginModule> +PepperPluginDelegateImpl::CreatePepperPlugin(const FilePath& path) { + // See if a module has already been loaded for this plugin. + scoped_refptr<webkit::ppapi::PluginModule> module = + PepperPluginRegistry::GetInstance()->GetLiveModule(path); + if (module) + return module; + + // In-process plugins will have always been created up-front to avoid the + // sandbox restrictions. So gettin here implies it doesn't exist or should + // be out of process. + const PepperPluginInfo* info = + PepperPluginRegistry::GetInstance()->GetInfoForPlugin(path); + if (!info || !info->is_out_of_process) + return module; // Return the NULL module. + + // Out of process: have the browser start the plugin process for us. + base::ProcessHandle plugin_process_handle = base::kNullProcessHandle; + IPC::ChannelHandle channel_handle; + render_view_->Send(new ViewHostMsg_OpenChannelToPepperPlugin( + path, &plugin_process_handle, &channel_handle)); + if (channel_handle.name.empty()) { + // Couldn't be initialized. + return scoped_refptr<webkit::ppapi::PluginModule>(); + } + + // Create a new HostDispatcher for the proxying, and hook it to a new + // PluginModule. Note that AddLiveModule must be called before any early + // returns since the module's destructor will remove itself. + module = new webkit::ppapi::PluginModule(info->name, + PepperPluginRegistry::GetInstance()); + PepperPluginRegistry::GetInstance()->AddLiveModule(path, module); + scoped_ptr<DispatcherWrapper> dispatcher(new DispatcherWrapper); + if (!dispatcher->Init( + plugin_process_handle, channel_handle, + module->pp_module(), + webkit::ppapi::PluginModule::GetLocalGetInterfaceFunc())) + return scoped_refptr<webkit::ppapi::PluginModule>(); + module->InitAsProxied(dispatcher.release()); + return module; +} + +void PepperPluginDelegateImpl::ViewInitiatedPaint() { + // Notify all of our instances that we started painting. This is used for + // internal bookkeeping only, so we know that the set can not change under + // us. + for (std::set<webkit::ppapi::PluginInstance*>::iterator i = + active_instances_.begin(); + i != active_instances_.end(); ++i) + (*i)->ViewInitiatedPaint(); +} + +void PepperPluginDelegateImpl::ViewFlushedPaint() { + // Notify all instances that we painted. This will call into the plugin, and + // we it may ask to close itself as a result. This will, in turn, modify our + // set, possibly invalidating the iterator. So we iterate on a copy that + // won't change out from under us. + std::set<webkit::ppapi::PluginInstance*> plugins = active_instances_; + for (std::set<webkit::ppapi::PluginInstance*>::iterator i = plugins.begin(); + i != plugins.end(); ++i) { + // The copy above makes sure our iterator is never invalid if some plugins + // are destroyed. But some plugin may decide to close all of its views in + // response to a paint in one of them, so we need to make sure each one is + // still "current" before using it. + // + // It's possible that a plugin was destroyed, but another one was created + // with the same address. In this case, we'll call ViewFlushedPaint on that + // new plugin. But that's OK for this particular case since we're just + // notifying all of our instances that the view flushed, and the new one is + // one of our instances. + // + // What about the case where a new one is created in a callback at a new + // address and we don't issue the callback? We're still OK since this + // callback is used for flush callbacks and we could not have possibly + // started a new paint (ViewInitiatedPaint) for the new plugin while + // processing a previous paint for an existing one. + if (active_instances_.find(*i) != active_instances_.end()) + (*i)->ViewFlushedPaint(); + } +} + +webkit::ppapi::PluginInstance* +PepperPluginDelegateImpl::GetBitmapForOptimizedPluginPaint( + const gfx::Rect& paint_bounds, + TransportDIB** dib, + gfx::Rect* location, + gfx::Rect* clip) { + for (std::set<webkit::ppapi::PluginInstance*>::iterator i = + active_instances_.begin(); + i != active_instances_.end(); ++i) { + webkit::ppapi::PluginInstance* instance = *i; + if (instance->GetBitmapForOptimizedPluginPaint( + paint_bounds, dib, location, clip)) + return *i; + } + return NULL; +} + +void PepperPluginDelegateImpl::InstanceCreated( + webkit::ppapi::PluginInstance* instance) { + active_instances_.insert(instance); + + // Set the initial focus. + instance->SetContentAreaFocus(render_view_->has_focus()); +} + +void PepperPluginDelegateImpl::InstanceDeleted( + webkit::ppapi::PluginInstance* instance) { + active_instances_.erase(instance); +} + +webkit::ppapi::PluginDelegate::PlatformImage2D* +PepperPluginDelegateImpl::CreateImage2D(int width, int height) { + uint32 buffer_size = width * height * 4; + + // Allocate the transport DIB and the PlatformCanvas pointing to it. +#if defined(OS_MACOSX) + // On the Mac, shared memory has to be created in the browser in order to + // work in the sandbox. Do this by sending a message to the browser + // requesting a TransportDIB (see also + // chrome/renderer/webplugin_delegate_proxy.cc, method + // WebPluginDelegateProxy::CreateBitmap() for similar code). The TransportDIB + // is cached in the browser, and is freed (in typical cases) by the + // PlatformImage2DImpl's destructor. + TransportDIB::Handle dib_handle; + IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(buffer_size, + true, + &dib_handle); + if (!RenderThread::current()->Send(msg)) + return NULL; + if (!TransportDIB::is_valid(dib_handle)) + return NULL; + + TransportDIB* dib = TransportDIB::Map(dib_handle); +#else + static int next_dib_id = 0; + TransportDIB* dib = TransportDIB::Create(buffer_size, next_dib_id++); + if (!dib) + return NULL; +#endif + + return new PlatformImage2DImpl(width, height, dib); +} + +webkit::ppapi::PluginDelegate::PlatformContext3D* + PepperPluginDelegateImpl::CreateContext3D() { +#ifdef ENABLE_GPU + // If accelerated compositing of plugins is disabled, fail to create a 3D + // context, because it won't be visible. This allows graceful fallback in the + // modules. + if (!render_view_->webkit_preferences().accelerated_plugins_enabled) + return NULL; + WebGraphicsContext3DCommandBufferImpl* context = + static_cast<WebGraphicsContext3DCommandBufferImpl*>( + render_view_->webview()->graphicsContext3D()); + if (!context) + return NULL; + + ggl::Context* parent_context = context->context(); + if (!parent_context) + return NULL; + + return new PlatformContext3DImpl(parent_context); +#else + return NULL; +#endif +} + +webkit::ppapi::PluginDelegate::PlatformVideoDecoder* +PepperPluginDelegateImpl::CreateVideoDecoder( + const PP_VideoDecoderConfig_Dev& decoder_config) { + scoped_ptr<PlatformVideoDecoderImpl> decoder(new PlatformVideoDecoderImpl()); + + if (!decoder->Init(decoder_config)) + return NULL; + + return decoder.release(); +} + +void PepperPluginDelegateImpl::NumberOfFindResultsChanged(int identifier, + int total, + bool final_result) { + render_view_->reportFindInPageMatchCount(identifier, total, final_result); +} + +void PepperPluginDelegateImpl::SelectedFindResultChanged(int identifier, + int index) { + render_view_->reportFindInPageSelection( + identifier, index + 1, WebKit::WebRect()); +} + +webkit::ppapi::PluginDelegate::PlatformAudio* +PepperPluginDelegateImpl::CreateAudio( + uint32_t sample_rate, uint32_t sample_count, + webkit::ppapi::PluginDelegate::PlatformAudio::Client* client) { + scoped_refptr<PlatformAudioImpl> audio( + new PlatformAudioImpl(render_view_->audio_message_filter())); + if (audio->Initialize(sample_rate, sample_count, client)) { + // Balanced by Release invoked in PlatformAudioImpl::ShutDownOnIOThread(). + return audio.release(); + } else { + return NULL; + } +} + +bool PepperPluginDelegateImpl::RunFileChooser( + const WebKit::WebFileChooserParams& params, + WebKit::WebFileChooserCompletion* chooser_completion) { + return render_view_->runFileChooser(params, chooser_completion); +} + +bool PepperPluginDelegateImpl::AsyncOpenFile(const FilePath& path, + int flags, + AsyncOpenFileCallback* callback) { + int message_id = id_generator_++; + DCHECK(!messages_waiting_replies_.Lookup(message_id)); + messages_waiting_replies_.AddWithID(callback, message_id); + IPC::Message* msg = new ViewHostMsg_AsyncOpenFile( + render_view_->routing_id(), path, flags, message_id); + return render_view_->Send(msg); +} + +void PepperPluginDelegateImpl::OnAsyncFileOpened( + base::PlatformFileError error_code, + base::PlatformFile file, + int message_id) { + AsyncOpenFileCallback* callback = + messages_waiting_replies_.Lookup(message_id); + DCHECK(callback); + messages_waiting_replies_.Remove(message_id); + callback->Run(error_code, file); + delete callback; +} + +void PepperPluginDelegateImpl::OnSetFocus(bool has_focus) { + for (std::set<webkit::ppapi::PluginInstance*>::iterator i = + active_instances_.begin(); + i != active_instances_.end(); ++i) + (*i)->SetContentAreaFocus(has_focus); +} + +bool PepperPluginDelegateImpl::OpenFileSystem( + const GURL& url, + fileapi::FileSystemType type, + long long size, + fileapi::FileSystemCallbackDispatcher* dispatcher) { + FileSystemDispatcher* file_system_dispatcher = + ChildThread::current()->file_system_dispatcher(); + return file_system_dispatcher->OpenFileSystem( + url, type, size, true /* create */, dispatcher); +} + +bool PepperPluginDelegateImpl::MakeDirectory( + const FilePath& path, + bool recursive, + fileapi::FileSystemCallbackDispatcher* dispatcher) { + FileSystemDispatcher* file_system_dispatcher = + ChildThread::current()->file_system_dispatcher(); + return file_system_dispatcher->Create( + path, false, true, recursive, dispatcher); +} + +bool PepperPluginDelegateImpl::Query( + const FilePath& path, + fileapi::FileSystemCallbackDispatcher* dispatcher) { + FileSystemDispatcher* file_system_dispatcher = + ChildThread::current()->file_system_dispatcher(); + return file_system_dispatcher->ReadMetadata(path, dispatcher); +} + +bool PepperPluginDelegateImpl::Touch( + const FilePath& path, + const base::Time& last_access_time, + const base::Time& last_modified_time, + fileapi::FileSystemCallbackDispatcher* dispatcher) { + FileSystemDispatcher* file_system_dispatcher = + ChildThread::current()->file_system_dispatcher(); + return file_system_dispatcher->TouchFile(path, last_access_time, + last_modified_time, dispatcher); +} + +bool PepperPluginDelegateImpl::Delete( + const FilePath& path, + fileapi::FileSystemCallbackDispatcher* dispatcher) { + FileSystemDispatcher* file_system_dispatcher = + ChildThread::current()->file_system_dispatcher(); + return file_system_dispatcher->Remove(path, false /* recursive */, + dispatcher); +} + +bool PepperPluginDelegateImpl::Rename( + const FilePath& file_path, + const FilePath& new_file_path, + fileapi::FileSystemCallbackDispatcher* dispatcher) { + FileSystemDispatcher* file_system_dispatcher = + ChildThread::current()->file_system_dispatcher(); + return file_system_dispatcher->Move(file_path, new_file_path, dispatcher); +} + +bool PepperPluginDelegateImpl::ReadDirectory( + const FilePath& directory_path, + fileapi::FileSystemCallbackDispatcher* dispatcher) { + FileSystemDispatcher* file_system_dispatcher = + ChildThread::current()->file_system_dispatcher(); + return file_system_dispatcher->ReadDirectory(directory_path, dispatcher); +} + +base::PlatformFileError PepperPluginDelegateImpl::OpenFile( + const webkit::ppapi::PepperFilePath& path, + int flags, + base::PlatformFile* file) { + IPC::PlatformFileForTransit transit_file; + base::PlatformFileError error; + IPC::Message* msg = new PepperFileMsg_OpenFile( + path, flags, &error, &transit_file); + if (!render_view_->Send(msg)) { + *file = base::kInvalidPlatformFileValue; + return base::PLATFORM_FILE_ERROR_FAILED; + } + *file = IPC::PlatformFileForTransitToPlatformFile(transit_file); + return error; +} + +base::PlatformFileError PepperPluginDelegateImpl::RenameFile( + const webkit::ppapi::PepperFilePath& from_path, + const webkit::ppapi::PepperFilePath& to_path) { + base::PlatformFileError error; + IPC::Message* msg = new PepperFileMsg_RenameFile(from_path, to_path, &error); + if (!render_view_->Send(msg)) + return base::PLATFORM_FILE_ERROR_FAILED; + return error; +} + +base::PlatformFileError PepperPluginDelegateImpl::DeleteFileOrDir( + const webkit::ppapi::PepperFilePath& path, + bool recursive) { + base::PlatformFileError error; + IPC::Message* msg = new PepperFileMsg_DeleteFileOrDir( + path, recursive, &error); + if (!render_view_->Send(msg)) + return base::PLATFORM_FILE_ERROR_FAILED; + return error; +} + +base::PlatformFileError PepperPluginDelegateImpl::CreateDir( + const webkit::ppapi::PepperFilePath& path) { + base::PlatformFileError error; + IPC::Message* msg = new PepperFileMsg_CreateDir(path, &error); + if (!render_view_->Send(msg)) + return base::PLATFORM_FILE_ERROR_FAILED; + return error; +} + +base::PlatformFileError PepperPluginDelegateImpl::QueryFile( + const webkit::ppapi::PepperFilePath& path, + base::PlatformFileInfo* info) { + base::PlatformFileError error; + IPC::Message* msg = new PepperFileMsg_QueryFile(path, info, &error); + if (!render_view_->Send(msg)) + return base::PLATFORM_FILE_ERROR_FAILED; + return error; +} + +base::PlatformFileError PepperPluginDelegateImpl::GetDirContents( + const webkit::ppapi::PepperFilePath& path, + webkit::ppapi::DirContents* contents) { + base::PlatformFileError error; + IPC::Message* msg = new PepperFileMsg_GetDirContents(path, contents, &error); + if (!render_view_->Send(msg)) + return base::PLATFORM_FILE_ERROR_FAILED; + return error; +} + +scoped_refptr<base::MessageLoopProxy> +PepperPluginDelegateImpl::GetFileThreadMessageLoopProxy() { + return RenderThread::current()->GetFileThreadMessageLoopProxy(); +} + +int32_t PepperPluginDelegateImpl::ConnectTcp( + webkit::ppapi::PPB_Flash_NetConnector_Impl* connector, + const char* host, + uint16_t port) { + int request_id = pending_connect_tcps_.Add( + new scoped_refptr<webkit::ppapi::PPB_Flash_NetConnector_Impl>(connector)); + IPC::Message* msg = + new PepperMsg_ConnectTcp(render_view_->routing_id(), + request_id, + std::string(host), + port); + if (!render_view_->Send(msg)) { + pending_connect_tcps_.Remove(request_id); + return PP_ERROR_FAILED; + } + + return PP_ERROR_WOULDBLOCK; +} + +int32_t PepperPluginDelegateImpl::ConnectTcpAddress( + webkit::ppapi::PPB_Flash_NetConnector_Impl* connector, + const struct PP_Flash_NetAddress* addr) { + int request_id = pending_connect_tcps_.Add( + new scoped_refptr<webkit::ppapi::PPB_Flash_NetConnector_Impl>(connector)); + IPC::Message* msg = + new PepperMsg_ConnectTcpAddress(render_view_->routing_id(), + request_id, + *addr); + if (!render_view_->Send(msg)) { + pending_connect_tcps_.Remove(request_id); + return PP_ERROR_FAILED; + } + + return PP_ERROR_WOULDBLOCK; +} + +void PepperPluginDelegateImpl::OnConnectTcpACK( + int request_id, + base::PlatformFile socket, + const PP_Flash_NetAddress& local_addr, + const PP_Flash_NetAddress& remote_addr) { + scoped_refptr<webkit::ppapi::PPB_Flash_NetConnector_Impl> connector = + *pending_connect_tcps_.Lookup(request_id); + pending_connect_tcps_.Remove(request_id); + + connector->CompleteConnectTcp(socket, local_addr, remote_addr); +} + +int32_t PepperPluginDelegateImpl::ShowContextMenu( + webkit::ppapi::PPB_Flash_Menu_Impl* menu, + const gfx::Point& position) { + int request_id = pending_context_menus_.Add( + new scoped_refptr<webkit::ppapi::PPB_Flash_Menu_Impl>(menu)); + + ContextMenuParams params; + params.x = position.x(); + params.y = position.y(); + params.custom_context.is_pepper_menu = true; + params.custom_context.request_id = request_id; + params.custom_items = menu->menu_data(); + + IPC::Message* msg = new ViewHostMsg_ContextMenu(render_view_->routing_id(), + params); + if (!render_view_->Send(msg)) { + pending_context_menus_.Remove(request_id); + return PP_ERROR_FAILED; + } + + return PP_ERROR_WOULDBLOCK; +} + +void PepperPluginDelegateImpl::OnContextMenuClosed( + const webkit_glue::CustomContextMenuContext& custom_context) { + int request_id = custom_context.request_id; + scoped_refptr<webkit::ppapi::PPB_Flash_Menu_Impl> menu = + *pending_context_menus_.Lookup(request_id); + if (!menu) { + NOTREACHED() << "CompleteShowContextMenu() called twice for the same menu."; + return; + } + pending_context_menus_.Remove(request_id); + + if (has_saved_context_menu_action_) { + menu->CompleteShow(PP_OK, saved_context_menu_action_); + has_saved_context_menu_action_ = false; + saved_context_menu_action_ = 0; + } else { + menu->CompleteShow(PP_ERROR_USERCANCEL, 0); + } +} + +void PepperPluginDelegateImpl::OnCustomContextMenuAction( + const webkit_glue::CustomContextMenuContext& custom_context, + unsigned action) { + // Just save the action. + DCHECK(!has_saved_context_menu_action_); + has_saved_context_menu_action_ = true; + saved_context_menu_action_ = action; +} + +webkit::ppapi::FullscreenContainer* +PepperPluginDelegateImpl::CreateFullscreenContainer( + webkit::ppapi::PluginInstance* instance) { + return render_view_->CreatePepperFullscreenContainer(instance); +} + +gfx::Size PepperPluginDelegateImpl::GetScreenSize() { + WebKit::WebScreenInfo info = render_view_->screenInfo(); + return gfx::Size(info.rect.width, info.rect.height); +} + +std::string PepperPluginDelegateImpl::GetDefaultEncoding() { + // TODO(brettw) bug 56615: Somehow get the preference for the default + // encoding here rather than using the global default for the UI language. + return content::GetContentClient()->renderer()->GetDefaultEncoding(); +} + +void PepperPluginDelegateImpl::ZoomLimitsChanged(double minimum_factor, + double maximum_factor) { + double minimum_level = WebView::zoomFactorToZoomLevel(minimum_factor); + double maximum_level = WebView::zoomFactorToZoomLevel(maximum_factor); + render_view_->webview()->zoomLimitsChanged(minimum_level, maximum_level); +} + +std::string PepperPluginDelegateImpl::ResolveProxy(const GURL& url) { + int net_error; + std::string proxy_result; + RenderThread::current()->Send( + new ChildProcessHostMsg_ResolveProxy(url, &net_error, &proxy_result)); + return proxy_result; +} + +void PepperPluginDelegateImpl::DidStartLoading() { + render_view_->DidStartLoadingForPlugin(); +} + +void PepperPluginDelegateImpl::DidStopLoading() { + render_view_->DidStopLoadingForPlugin(); +} + +void PepperPluginDelegateImpl::SetContentRestriction(int restrictions) { + render_view_->Send(new ViewHostMsg_UpdateContentRestrictions( + render_view_->routing_id(), restrictions)); +} + +void PepperPluginDelegateImpl::HasUnsupportedFeature() { + render_view_->Send(new ViewHostMsg_PDFHasUnsupportedFeature( + render_view_->routing_id())); +} + +P2PSocketDispatcher* PepperPluginDelegateImpl::GetP2PSocketDispatcher() { + return render_view_->p2p_socket_dispatcher(); +} diff --git a/content/renderer/pepper_plugin_delegate_impl.h b/content/renderer/pepper_plugin_delegate_impl.h new file mode 100644 index 0000000..5def533 --- /dev/null +++ b/content/renderer/pepper_plugin_delegate_impl.h @@ -0,0 +1,202 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_PEPPER_PLUGIN_DELEGATE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PLUGIN_DELEGATE_IMPL_H_ +#pragma once + +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/id_map.h" +#include "base/ref_counted.h" +#include "base/weak_ptr.h" +#include "webkit/plugins/ppapi/plugin_delegate.h" +#include "webkit/plugins/ppapi/ppb_flash_menu_impl.h" + +class FilePath; +class RenderView; + +namespace gfx { +class Point; +class Rect; +} + +namespace webkit { +namespace ppapi { +class PepperFilePath; +class PluginInstance; +class PluginModule; +} +} + +namespace WebKit { +class WebFileChooserCompletion; +struct WebFileChooserParams; +} + +namespace webkit_glue { +struct CustomContextMenuContext; +} + +class TransportDIB; + +class PepperPluginDelegateImpl + : public webkit::ppapi::PluginDelegate, + public base::SupportsWeakPtr<PepperPluginDelegateImpl> { + public: + explicit PepperPluginDelegateImpl(RenderView* render_view); + virtual ~PepperPluginDelegateImpl(); + + scoped_refptr<webkit::ppapi::PluginModule> CreatePepperPlugin( + const FilePath& path); + + // Called by RenderView to tell us about painting events, these two functions + // just correspond to the DidInitiatePaint and DidFlushPaint in R.V.. + void ViewInitiatedPaint(); + void ViewFlushedPaint(); + + // Called by RenderView to implement the corresponding function in its base + // class RenderWidget (see that for more). + webkit::ppapi::PluginInstance* GetBitmapForOptimizedPluginPaint( + const gfx::Rect& paint_bounds, + TransportDIB** dib, + gfx::Rect* location, + gfx::Rect* clip); + + // Called by RenderView when ViewMsg_AsyncOpenFile_ACK. + void OnAsyncFileOpened(base::PlatformFileError error_code, + base::PlatformFile file, + int message_id); + + // Notification that the render view has been focused or defocused. This + // notifies all of the plugins. + void OnSetFocus(bool has_focus); + + // PluginDelegate implementation. + virtual void InstanceCreated( + webkit::ppapi::PluginInstance* instance); + virtual void InstanceDeleted( + webkit::ppapi::PluginInstance* instance); + virtual PlatformAudio* CreateAudio( + uint32_t sample_rate, + uint32_t sample_count, + PlatformAudio::Client* client); + virtual PlatformImage2D* CreateImage2D(int width, int height); + virtual PlatformContext3D* CreateContext3D(); + virtual PlatformVideoDecoder* CreateVideoDecoder( + const PP_VideoDecoderConfig_Dev& decoder_config); + virtual void NumberOfFindResultsChanged(int identifier, + int total, + bool final_result); + virtual void SelectedFindResultChanged(int identifier, int index); + virtual bool RunFileChooser( + const WebKit::WebFileChooserParams& params, + WebKit::WebFileChooserCompletion* chooser_completion); + virtual bool AsyncOpenFile(const FilePath& path, + int flags, + AsyncOpenFileCallback* callback); + virtual bool OpenFileSystem( + const GURL& url, + fileapi::FileSystemType type, + long long size, + fileapi::FileSystemCallbackDispatcher* dispatcher); + virtual bool MakeDirectory(const FilePath& path, + bool recursive, + fileapi::FileSystemCallbackDispatcher* dispatcher); + virtual bool Query(const FilePath& path, + fileapi::FileSystemCallbackDispatcher* dispatcher); + virtual bool Touch(const FilePath& path, + const base::Time& last_access_time, + const base::Time& last_modified_time, + fileapi::FileSystemCallbackDispatcher* dispatcher); + virtual bool Delete(const FilePath& path, + fileapi::FileSystemCallbackDispatcher* dispatcher); + virtual bool Rename(const FilePath& file_path, + const FilePath& new_file_path, + fileapi::FileSystemCallbackDispatcher* dispatcher); + virtual bool ReadDirectory(const FilePath& directory_path, + fileapi::FileSystemCallbackDispatcher* dispatcher); + virtual base::PlatformFileError OpenFile( + const webkit::ppapi::PepperFilePath& path, + int flags, + base::PlatformFile* file); + virtual base::PlatformFileError RenameFile( + const webkit::ppapi::PepperFilePath& from_path, + const webkit::ppapi::PepperFilePath& to_path); + virtual base::PlatformFileError DeleteFileOrDir( + const webkit::ppapi::PepperFilePath& path, + bool recursive); + virtual base::PlatformFileError CreateDir( + const webkit::ppapi::PepperFilePath& path); + virtual base::PlatformFileError QueryFile( + const webkit::ppapi::PepperFilePath& path, + base::PlatformFileInfo* info); + virtual base::PlatformFileError GetDirContents( + const webkit::ppapi::PepperFilePath& path, + webkit::ppapi::DirContents* contents); + virtual scoped_refptr<base::MessageLoopProxy> GetFileThreadMessageLoopProxy(); + virtual int32_t ConnectTcp( + webkit::ppapi::PPB_Flash_NetConnector_Impl* connector, + const char* host, + uint16_t port); + virtual int32_t ConnectTcpAddress( + webkit::ppapi::PPB_Flash_NetConnector_Impl* connector, + const struct PP_Flash_NetAddress* addr); + // This is the completion for both |ConnectTcp()| and |ConnectTcpAddress()|. + void OnConnectTcpACK( + int request_id, + base::PlatformFile socket, + const PP_Flash_NetAddress& local_addr, + const PP_Flash_NetAddress& remote_addr); + virtual int32_t ShowContextMenu( + webkit::ppapi::PPB_Flash_Menu_Impl* menu, + const gfx::Point& position); + void OnContextMenuClosed( + const webkit_glue::CustomContextMenuContext& custom_context); + void OnCustomContextMenuAction( + const webkit_glue::CustomContextMenuContext& custom_context, + unsigned action); + void CompleteShowContextMenu(int request_id, + bool did_select, + unsigned action); + virtual webkit::ppapi::FullscreenContainer* + CreateFullscreenContainer( + webkit::ppapi::PluginInstance* instance); + virtual gfx::Size GetScreenSize(); + virtual std::string GetDefaultEncoding(); + virtual void ZoomLimitsChanged(double minimum_factor, double maximum_factor); + virtual std::string ResolveProxy(const GURL& url); + virtual void DidStartLoading(); + virtual void DidStopLoading(); + virtual void SetContentRestriction(int restrictions); + virtual void HasUnsupportedFeature(); + virtual P2PSocketDispatcher* GetP2PSocketDispatcher(); + + private: + // Pointer to the RenderView that owns us. + RenderView* render_view_; + + std::set<webkit::ppapi::PluginInstance*> active_instances_; + + // Used to send a single context menu "completion" upon menu close. + bool has_saved_context_menu_action_; + unsigned saved_context_menu_action_; + + // TODO(viettrungluu): Get rid of |id_generator_| -- just use |IDMap::Add()|. + // Rename |messages_waiting_replies_| (to specify async open file). + int id_generator_; + IDMap<AsyncOpenFileCallback> messages_waiting_replies_; + + IDMap<scoped_refptr<webkit::ppapi::PPB_Flash_NetConnector_Impl>, + IDMapOwnPointer> pending_connect_tcps_; + + IDMap<scoped_refptr<webkit::ppapi::PPB_Flash_Menu_Impl>, + IDMapOwnPointer> pending_context_menus_; + + DISALLOW_COPY_AND_ASSIGN(PepperPluginDelegateImpl); +}; + +#endif // CONTENT_RENDERER_PEPPER_PLUGIN_DELEGATE_IMPL_H_ diff --git a/content/renderer/web_ui_bindings.cc b/content/renderer/web_ui_bindings.cc new file mode 100644 index 0000000..4d555da --- /dev/null +++ b/content/renderer/web_ui_bindings.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/web_ui_bindings.h" + +#include "base/json/json_writer.h" +#include "base/scoped_ptr.h" +#include "base/stl_util-inl.h" +#include "base/values.h" +#include "chrome/common/render_messages.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" + +namespace { + +// Creates a Value which is a copy of the CppVariant |value|. All objects are +// treated as Lists for now since CppVariant does not distinguish arrays in any +// convenient way and we currently have no need of non array objects. +Value* CreateValueFromCppVariant(const CppVariant& value) { + if (value.isBool()) + return Value::CreateBooleanValue(value.ToBoolean()); + if (value.isDouble()) + return Value::CreateDoubleValue(value.ToDouble()); + if (value.isInt32()) + return Value::CreateIntegerValue(value.ToInt32()); + if (value.isString()) + return Value::CreateStringValue(value.ToString()); + + if (value.isObject()) { + // We currently assume all objects are arrays. + std::vector<CppVariant> vector = value.ToVector(); + ListValue* list = new ListValue(); + for (size_t i = 0; i < vector.size(); ++i) { + list->Append(CreateValueFromCppVariant(vector[i])); + } + return list; + } + + // Covers null and undefined. + return Value::CreateNullValue(); +} + +} // namespace + +DOMBoundBrowserObject::DOMBoundBrowserObject() + : sender_(NULL), + routing_id_(0) { +} + +DOMBoundBrowserObject::~DOMBoundBrowserObject() { + STLDeleteContainerPointers(properties_.begin(), properties_.end()); +} + +WebUIBindings::WebUIBindings() { + BindMethod("send", &WebUIBindings::send); +} + +WebUIBindings::~WebUIBindings() {} + +void WebUIBindings::send(const CppArgumentList& args, CppVariant* result) { + // We expect at least a string message identifier, and optionally take + // an object parameter. If we get anything else we bail. + if (args.size() < 1 || args.size() > 2) + return; + + // Require the first parameter to be the message name. + if (!args[0].isString()) + return; + const std::string message = args[0].ToString(); + + // If they've provided an optional message parameter, convert that into JSON. + std::string content; + if (args.size() == 2) { + if (!args[1].isObject()) + return; + + scoped_ptr<Value> value(CreateValueFromCppVariant(args[1])); + base::JSONWriter::Write(value.get(), /* pretty_print= */ false, &content); + } + + // Retrieve the source frame's url + GURL source_url; + WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); + if (webframe) + source_url = webframe->url(); + + // Send the message up to the browser. + sender()->Send( + new ViewHostMsg_WebUISend(routing_id(), source_url, message, content)); +} + +void DOMBoundBrowserObject::SetProperty(const std::string& name, + const std::string& value) { + CppVariant* cpp_value = new CppVariant; + cpp_value->Set(value); + BindProperty(name, cpp_value); + properties_.push_back(cpp_value); +} diff --git a/content/renderer/web_ui_bindings.h b/content/renderer/web_ui_bindings.h new file mode 100644 index 0000000..0d49c96 --- /dev/null +++ b/content/renderer/web_ui_bindings.h @@ -0,0 +1,68 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_WEB_UI_BINDINGS_H_ +#define CONTENT_RENDERER_WEB_UI_BINDINGS_H_ +#pragma once + +#include "ipc/ipc_message.h" +#include "webkit/glue/cpp_bound_class.h" + +// A DOMBoundBrowserObject is a backing for some object bound to the window +// in JS that knows how to dispatch messages to an associated c++ object living +// in the browser process. +class DOMBoundBrowserObject : public CppBoundClass { + public: + DOMBoundBrowserObject(); + virtual ~DOMBoundBrowserObject(); + + // Set the message channel back to the browser. + void set_message_sender(IPC::Message::Sender* sender) { + sender_ = sender; + } + + // Set the routing id for messages back to the browser. + void set_routing_id(int routing_id) { + routing_id_ = routing_id; + } + + IPC::Message::Sender* sender() { return sender_; } + int routing_id() { return routing_id_; } + + // Sets a property with the given name and value. + void SetProperty(const std::string& name, const std::string& value); + + private: + // Our channel back to the browser is a message sender + // and routing id. + IPC::Message::Sender* sender_; + int routing_id_; + + // The list of properties that have been set. We keep track of this so we + // can free them on destruction. + typedef std::vector<CppVariant*> PropertyList; + PropertyList properties_; + + DISALLOW_COPY_AND_ASSIGN(DOMBoundBrowserObject); +}; + +// WebUIBindings is the class backing the "chrome" object accessible +// from Javascript from privileged pages. +// +// We expose one function, for sending a message to the browser: +// send(String name, Object argument); +// It's plumbed through to the OnWebUIMessage callback on RenderViewHost +// delegate. +class WebUIBindings : public DOMBoundBrowserObject { + public: + WebUIBindings(); + virtual ~WebUIBindings(); + + // The send() function provided to Javascript. + void send(const CppArgumentList& args, CppVariant* result); + private: + DISALLOW_COPY_AND_ASSIGN(WebUIBindings); +}; + +#endif // CONTENT_RENDERER_WEB_UI_BINDINGS_H_ |