diff options
Diffstat (limited to 'chromium/content/browser')
637 files changed, 23279 insertions, 14619 deletions
diff --git a/chromium/content/browser/DEPS b/chromium/content/browser/DEPS index 39be9891906..2a42e40f7e3 100644 --- a/chromium/content/browser/DEPS +++ b/chromium/content/browser/DEPS @@ -1,5 +1,4 @@ include_rules = [ - "+content/gpu", # For gpu_info_collector.h and in-process GPU "+content/port/browser", "+content/public/browser", "+content/child/webkitplatformsupport_impl.h", # For in-process webkit @@ -10,10 +9,6 @@ include_rules = [ "+ui/webui", "+win8/util", - # For single-process mode. - "+content/child/child_process.h", - "+content/utility/utility_thread_impl.h", - # TODO(joi): This was misplaced; need to move it somewhere else, # since //content shouldn't depend on //components, which is a layer # above. @@ -45,6 +40,7 @@ include_rules = [ "+third_party/WebKit/public/platform/WebScreenInfo.h", "+third_party/WebKit/public/platform/WebString.h", "+third_party/WebKit/public/platform/WebVibration.h", + "+third_party/WebKit/public/web/WebAXEnums.h", "+third_party/WebKit/public/web/WebCompositionUnderline.h", "+third_party/WebKit/public/web/WebConsoleMessage.h", "+third_party/WebKit/public/web/WebCursorInfo.h", @@ -61,7 +57,6 @@ include_rules = [ "+third_party/WebKit/public/web/WebTextDirection.h", # These should be burned down. http://crbug.com/237267 - "!third_party/WebKit/public/web/gtk/WebInputEventFactory.h", "!third_party/WebKit/public/web/mac/WebInputEventFactory.h", # DO NOT ADD ANY CHROME OR COMPONENTS INCLUDES HERE!!! diff --git a/chromium/content/browser/accessibility/accessibility_ui.cc b/chromium/content/browser/accessibility/accessibility_ui.cc index 585be6806a1..d5a9734b0eb 100644 --- a/chromium/content/browser/accessibility/accessibility_ui.cc +++ b/chromium/content/browser/accessibility/accessibility_ui.cc @@ -22,6 +22,7 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui_data_source.h" #include "content/public/common/url_constants.h" @@ -91,15 +92,16 @@ void SendTargetsData( const WebUIDataSource::GotDataCallback& callback) { scoped_ptr<base::ListValue> rvh_list(new base::ListValue()); - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { // Ignore processes that don't have a connection, such as crashed tabs. - if (!widgets[i]->GetProcess()->HasConnection()) + if (!widget->GetProcess()->HasConnection()) continue; - if (!widgets[i]->IsRenderView()) + if (!widget->IsRenderView()) continue; - RenderViewHost* rvh = RenderViewHost::From(widgets[i]); + RenderViewHost* rvh = RenderViewHost::From(widget); rvh_list->Append(BuildTargetDescriptor(rvh)); } diff --git a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc index 0be9ba5067d..6d069954117 100644 --- a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc +++ b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc @@ -17,7 +17,7 @@ #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/accessibility_browser_test_utils.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" @@ -149,7 +149,7 @@ void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml( const std::string& html) { AccessibilityNotificationWaiter waiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationLoadComplete); + WebKit::WebAXEventLoadComplete); GURL html_data_url("data:text/html," + html); NavigateToURL(shell(), html_data_url); waiter.WaitForNotification(); @@ -504,7 +504,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, scoped_ptr<AccessibilityNotificationWaiter> waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationFocusChanged)); + WebKit::WebAXEventFocus)); ExecuteScript(L"document.body.children[0].focus()"); waiter->WaitForNotification(); @@ -516,7 +516,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, // Set the active descendant of the radio group waiter.reset(new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationFocusChanged)); + WebKit::WebAXEventFocus)); ExecuteScript( L"document.body.children[0].setAttribute('aria-activedescendant', 'li')"); waiter->WaitForNotification(); @@ -549,7 +549,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, scoped_ptr<AccessibilityNotificationWaiter> waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationCheckStateChanged)); + WebKit::WebAXEventCheckedStateChanged)); ExecuteScript(L"document.body.children[0].checked=true"); waiter->WaitForNotification(); @@ -577,7 +577,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationChildrenChanged)); + WebKit::WebAXEventChildrenChanged)); ExecuteScript(L"document.body.innerHTML='<b>new text</b>'"); waiter->WaitForNotification(); @@ -602,7 +602,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, scoped_ptr<AccessibilityNotificationWaiter> waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationChildrenChanged)); + WebKit::WebAXEventChildrenChanged)); ExecuteScript(L"document.body.children[0].style.visibility='visible'"); waiter->WaitForNotification(); @@ -635,7 +635,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, scoped_ptr<AccessibilityNotificationWaiter> waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationFocusChanged)); + WebKit::WebAXEventFocus)); ExecuteScript(L"document.body.children[0].focus()"); waiter->WaitForNotification(); @@ -649,7 +649,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, waiter.reset( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationBlur)); + WebKit::WebAXEventBlur)); base::win::ScopedComPtr<IAccessible> document_accessible( GetRendererAccessible()); ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); @@ -685,7 +685,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, scoped_ptr<AccessibilityNotificationWaiter> waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationValueChanged)); + WebKit::WebAXEventValueChanged)); ExecuteScript(L"document.body.children[0].value='new value'"); waiter->WaitForNotification(); diff --git a/chromium/content/browser/accessibility/browser_accessibility.cc b/chromium/content/browser/accessibility/browser_accessibility.cc index ae7efe2f660..e039cc71e69 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.cc +++ b/chromium/content/browser/accessibility/browser_accessibility.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/common/accessibility_messages.h" @@ -64,22 +65,20 @@ void BrowserAccessibility::InitializeTreeStructure( void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) { DCHECK_EQ(renderer_id_, src.id); - name_ = src.name; - value_ = src.value; role_ = src.role; state_ = src.state; string_attributes_ = src.string_attributes; int_attributes_ = src.int_attributes; float_attributes_ = src.float_attributes; bool_attributes_ = src.bool_attributes; + intlist_attributes_ = src.intlist_attributes; html_attributes_ = src.html_attributes; location_ = src.location; - indirect_child_ids_ = src.indirect_child_ids; - line_breaks_ = src.line_breaks; - cell_ids_ = src.cell_ids; - unique_cell_ids_ = src.unique_cell_ids; instance_active_ = true; + GetStringAttribute(AccessibilityNodeData::ATTR_NAME, &name_); + GetStringAttribute(AccessibilityNodeData::ATTR_VALUE, &value_); + PreInitialize(); } @@ -143,8 +142,8 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const { // nested web area. BrowserAccessibility* parent = parent_; bool need_to_offset_web_area = - (role_ == AccessibilityNodeData::ROLE_WEB_AREA || - role_ == AccessibilityNodeData::ROLE_ROOT_WEB_AREA); + (role_ == WebKit::WebAXRoleWebArea || + role_ == WebKit::WebAXRoleRootWebArea); while (parent) { if (need_to_offset_web_area && parent->location().width() > 0 && @@ -155,13 +154,13 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsRect() const { // On some platforms, we don't want to take the root scroll offsets // into account. - if (parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA && + if (parent->role() == WebKit::WebAXRoleRootWebArea && !manager()->UseRootScrollOffsetsWhenComputingBounds()) { break; } - if (parent->role() == AccessibilityNodeData::ROLE_WEB_AREA || - parent->role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA) { + if (parent->role() == WebKit::WebAXRoleWebArea || + parent->role() == WebKit::WebAXRoleRootWebArea) { int sx = 0; int sy = 0; if (parent->GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &sx) && @@ -212,7 +211,7 @@ void BrowserAccessibility::Destroy() { PostInitialize(); manager_->NotifyAccessibilityEvent( - AccessibilityNotificationObjectHide, this); + WebKit::WebAXEventHide, this); instance_active_ = false; manager_->RemoveNode(this); @@ -223,55 +222,197 @@ void BrowserAccessibility::NativeReleaseReference() { delete this; } +bool BrowserAccessibility::HasBoolAttribute(BoolAttribute attribute) const { + for (size_t i = 0; i < bool_attributes_.size(); ++i) { + if (bool_attributes_[i].first == attribute) + return true; + } + + return false; +} + + +bool BrowserAccessibility::GetBoolAttribute(BoolAttribute attribute) const { + for (size_t i = 0; i < bool_attributes_.size(); ++i) { + if (bool_attributes_[i].first == attribute) + return bool_attributes_[i].second; + } + + return false; +} + bool BrowserAccessibility::GetBoolAttribute( BoolAttribute attribute, bool* value) const { - BoolAttrMap::const_iterator iter = bool_attributes_.find(attribute); - if (iter != bool_attributes_.end()) { - *value = iter->second; - return true; + for (size_t i = 0; i < bool_attributes_.size(); ++i) { + if (bool_attributes_[i].first == attribute) { + *value = bool_attributes_[i].second; + return true; + } + } + + return false; +} + +bool BrowserAccessibility::HasFloatAttribute(FloatAttribute attribute) const { + for (size_t i = 0; i < float_attributes_.size(); ++i) { + if (float_attributes_[i].first == attribute) + return true; } return false; } +float BrowserAccessibility::GetFloatAttribute(FloatAttribute attribute) const { + for (size_t i = 0; i < float_attributes_.size(); ++i) { + if (float_attributes_[i].first == attribute) + return float_attributes_[i].second; + } + + return 0.0; +} + bool BrowserAccessibility::GetFloatAttribute( FloatAttribute attribute, float* value) const { - FloatAttrMap::const_iterator iter = float_attributes_.find(attribute); - if (iter != float_attributes_.end()) { - *value = iter->second; - return true; + for (size_t i = 0; i < float_attributes_.size(); ++i) { + if (float_attributes_[i].first == attribute) { + *value = float_attributes_[i].second; + return true; + } + } + + return false; +} + +bool BrowserAccessibility::HasIntAttribute(IntAttribute attribute) const { + for (size_t i = 0; i < int_attributes_.size(); ++i) { + if (int_attributes_[i].first == attribute) + return true; } return false; } +int BrowserAccessibility::GetIntAttribute(IntAttribute attribute) const { + for (size_t i = 0; i < int_attributes_.size(); ++i) { + if (int_attributes_[i].first == attribute) + return int_attributes_[i].second; + } + + return 0; +} + bool BrowserAccessibility::GetIntAttribute( IntAttribute attribute, int* value) const { - IntAttrMap::const_iterator iter = int_attributes_.find(attribute); - if (iter != int_attributes_.end()) { - *value = iter->second; - return true; + for (size_t i = 0; i < int_attributes_.size(); ++i) { + if (int_attributes_[i].first == attribute) { + *value = int_attributes_[i].second; + return true; + } } return false; } +bool BrowserAccessibility::HasStringAttribute(StringAttribute attribute) const { + for (size_t i = 0; i < string_attributes_.size(); ++i) { + if (string_attributes_[i].first == attribute) + return true; + } + + return false; +} + +const std::string& BrowserAccessibility::GetStringAttribute( + StringAttribute attribute) const { + CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ()); + for (size_t i = 0; i < string_attributes_.size(); ++i) { + if (string_attributes_[i].first == attribute) + return string_attributes_[i].second; + } + + return empty_string; +} + bool BrowserAccessibility::GetStringAttribute( + StringAttribute attribute, std::string* value) const { + for (size_t i = 0; i < string_attributes_.size(); ++i) { + if (string_attributes_[i].first == attribute) { + *value = string_attributes_[i].second; + return true; + } + } + + return false; +} + +string16 BrowserAccessibility::GetString16Attribute( + StringAttribute attribute) const { + std::string value_utf8; + if (!GetStringAttribute(attribute, &value_utf8)) + return string16(); + return UTF8ToUTF16(value_utf8); +} + +bool BrowserAccessibility::GetString16Attribute( StringAttribute attribute, string16* value) const { - StringAttrMap::const_iterator iter = string_attributes_.find(attribute); - if (iter != string_attributes_.end()) { - *value = iter->second; - return true; + std::string value_utf8; + if (!GetStringAttribute(attribute, &value_utf8)) + return false; + *value = UTF8ToUTF16(value_utf8); + return true; +} + +void BrowserAccessibility::SetStringAttribute( + StringAttribute attribute, const std::string& value) { + for (size_t i = 0; i < string_attributes_.size(); ++i) { + if (string_attributes_[i].first == attribute) { + string_attributes_[i].second = value; + return; + } + } + if (!value.empty()) + string_attributes_.push_back(std::make_pair(attribute, value)); +} + +bool BrowserAccessibility::HasIntListAttribute( + AccessibilityNodeData::IntListAttribute attribute) const { + for (size_t i = 0; i < intlist_attributes_.size(); ++i) { + if (intlist_attributes_[i].first == attribute) + return true; + } + + return false; +} + +const std::vector<int32>& BrowserAccessibility::GetIntListAttribute( + AccessibilityNodeData::IntListAttribute attribute) const { + CR_DEFINE_STATIC_LOCAL(std::vector<int32>, empty_vector, ()); + for (size_t i = 0; i < intlist_attributes_.size(); ++i) { + if (intlist_attributes_[i].first == attribute) + return intlist_attributes_[i].second; + } + + return empty_vector; +} + +bool BrowserAccessibility::GetIntListAttribute( + AccessibilityNodeData::IntListAttribute attribute, + std::vector<int32>* value) const { + for (size_t i = 0; i < intlist_attributes_.size(); ++i) { + if (intlist_attributes_[i].first == attribute) { + *value = intlist_attributes_[i].second; + return true; + } } return false; } bool BrowserAccessibility::GetHtmlAttribute( - const char* html_attr, string16* value) const { + const char* html_attr, std::string* value) const { for (size_t i = 0; i < html_attributes_.size(); i++) { - const string16& attr = html_attributes_[i].first; + const std::string& attr = html_attributes_[i].first; if (LowerCaseEqualsASCII(attr, html_attr)) { *value = html_attributes_[i].second; return true; @@ -281,6 +422,15 @@ bool BrowserAccessibility::GetHtmlAttribute( return false; } +bool BrowserAccessibility::GetHtmlAttribute( + const char* html_attr, string16* value) const { + std::string value_utf8; + if (!GetHtmlAttribute(html_attr, &value_utf8)) + return false; + *value = UTF8ToUTF16(value_utf8); + return true; +} + bool BrowserAccessibility::GetAriaTristate( const char* html_attr, bool* is_defined, @@ -306,33 +456,32 @@ bool BrowserAccessibility::GetAriaTristate( return false; // Not set } -bool BrowserAccessibility::HasState( - AccessibilityNodeData::State state_enum) const { +bool BrowserAccessibility::HasState(WebKit::WebAXState state_enum) const { return (state_ >> state_enum) & 1; } bool BrowserAccessibility::IsEditableText() const { // These roles don't have readonly set, but they're not editable text. - if (role_ == AccessibilityNodeData::ROLE_SCROLLAREA || - role_ == AccessibilityNodeData::ROLE_COLUMN || - role_ == AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER) { + if (role_ == WebKit::WebAXRoleScrollArea || + role_ == WebKit::WebAXRoleColumn || + role_ == WebKit::WebAXRoleTableHeaderContainer) { return false; } - // Note: STATE_READONLY being false means it's either a text control, + // Note: WebAXStateReadonly being false means it's either a text control, // or contenteditable. We also check for editable text roles to cover // another element that has role=textbox set on it. - return (!HasState(AccessibilityNodeData::STATE_READONLY) || - role_ == AccessibilityNodeData::ROLE_TEXT_FIELD || - role_ == AccessibilityNodeData::ROLE_TEXTAREA); + return (!HasState(WebKit::WebAXStateReadonly) || + role_ == WebKit::WebAXRoleTextField || + role_ == WebKit::WebAXRoleTextArea); } -string16 BrowserAccessibility::GetTextRecursive() const { +std::string BrowserAccessibility::GetTextRecursive() const { if (!name_.empty()) { return name_; } - string16 result; + std::string result; for (size_t i = 0; i < children_.size(); ++i) result += children_[i]->GetTextRecursive(); return result; diff --git a/chromium/content/browser/accessibility/browser_accessibility.h b/chromium/content/browser/accessibility/browser_accessibility.h index 8fda68fc079..a52075128a5 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.h +++ b/chromium/content/browser/accessibility/browser_accessibility.h @@ -10,9 +10,11 @@ #include <vector> #include "base/basictypes.h" +#include "base/strings/string16.h" #include "build/build_config.h" #include "content/common/accessibility_node_data.h" #include "content/common/content_export.h" +#include "third_party/WebKit/public/web/WebAXEnums.h" #if defined(OS_MACOSX) && __OBJC__ @class BrowserAccessibilityCocoa; @@ -26,12 +28,6 @@ class BrowserAccessibilityWin; class BrowserAccessibilityGtk; #endif -typedef std::map<AccessibilityNodeData::BoolAttribute, bool> BoolAttrMap; -typedef std::map<AccessibilityNodeData::FloatAttribute, float> FloatAttrMap; -typedef std::map<AccessibilityNodeData::IntAttribute, int> IntAttrMap; -typedef std::map<AccessibilityNodeData::StringAttribute, string16> - StringAttrMap; - //////////////////////////////////////////////////////////////////////////////// // // BrowserAccessibility @@ -136,49 +132,21 @@ class CONTENT_EXPORT BrowserAccessibility { // Accessors // - const BoolAttrMap& bool_attributes() const { - return bool_attributes_; - } - - const FloatAttrMap& float_attributes() const { - return float_attributes_; - } - - const IntAttrMap& int_attributes() const { - return int_attributes_; - } - - const StringAttrMap& string_attributes() const { - return string_attributes_; - } - const std::vector<BrowserAccessibility*>& children() const { return children_; } - const std::vector<std::pair<string16, string16> >& html_attributes() const { + const std::vector<std::pair<std::string, std::string> >& + html_attributes() const { return html_attributes_; } int32 index_in_parent() const { return index_in_parent_; } - const std::vector<int32>& indirect_child_ids() const { - return indirect_child_ids_; - } - const std::vector<int32>& line_breaks() const { - return line_breaks_; - } - const std::vector<int32>& cell_ids() const { - return cell_ids_; - } - const std::vector<int32>& unique_cell_ids() const { - return unique_cell_ids_; - } gfx::Rect location() const { return location_; } BrowserAccessibilityManager* manager() const { return manager_; } - const string16& name() const { return name_; } + const std::string& name() const { return name_; } int32 renderer_id() const { return renderer_id_; } int32 role() const { return role_; } - const string16& role_name() const { return role_name_; } int32 state() const { return state_; } - const string16& value() const { return value_; } + const std::string& value() const { return value_; } bool instance_active() const { return instance_active_; } #if defined(OS_MACOSX) && __OBJC__ @@ -189,29 +157,63 @@ class CONTENT_EXPORT BrowserAccessibility { BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(); #endif - // Retrieve the value of a bool attribute from the bool attribute - // map and returns true if found. - bool GetBoolAttribute( - AccessibilityNodeData::BoolAttribute attr, bool* value) const; - - // Retrieve the value of a float attribute from the float attribute - // map and returns true if found. + // Accessing accessibility attributes: + // + // There are dozens of possible attributes for an accessibility node, + // but only a few tend to apply to any one object, so we store them + // in sparse arrays of <attribute id, attribute value> pairs, organized + // by type (bool, int, float, string, int list). + // + // There are three accessors for each type of attribute: one that returns + // true if the attribute is present and false if not, one that takes a + // pointer argument and returns true if the attribute is present (if you + // need to distinguish between the default value and a missing attribute), + // and another that returns the default value for that type if the + // attribute is not present. In addition, strings can be returned as + // either std::string or string16, for convenience. + + bool HasBoolAttribute(AccessibilityNodeData::BoolAttribute attr) const; + bool GetBoolAttribute(AccessibilityNodeData::BoolAttribute attr) const; + bool GetBoolAttribute(AccessibilityNodeData::BoolAttribute attr, + bool* value) const; + + bool HasFloatAttribute(AccessibilityNodeData::FloatAttribute attr) const; + float GetFloatAttribute(AccessibilityNodeData::FloatAttribute attr) const; bool GetFloatAttribute(AccessibilityNodeData::FloatAttribute attr, float* value) const; - // Retrieve the value of an integer attribute from the integer attribute - // map and returns true if found. + bool HasIntAttribute(AccessibilityNodeData::IntAttribute attribute) const; + int GetIntAttribute(AccessibilityNodeData::IntAttribute attribute) const; bool GetIntAttribute(AccessibilityNodeData::IntAttribute attribute, int* value) const; - // Retrieve the value of a string attribute from the attribute map and - // returns true if found. - bool GetStringAttribute( - AccessibilityNodeData::StringAttribute attribute, string16* value) const; + bool HasStringAttribute( + AccessibilityNodeData::StringAttribute attribute) const; + const std::string& GetStringAttribute( + AccessibilityNodeData::StringAttribute attribute) const; + bool GetStringAttribute(AccessibilityNodeData::StringAttribute attribute, + std::string* value) const; + + bool GetString16Attribute(AccessibilityNodeData::StringAttribute attribute, + string16* value) const; + string16 GetString16Attribute( + AccessibilityNodeData::StringAttribute attribute) const; + + bool HasIntListAttribute( + AccessibilityNodeData::IntListAttribute attribute) const; + const std::vector<int32>& GetIntListAttribute( + AccessibilityNodeData::IntListAttribute attribute) const; + bool GetIntListAttribute(AccessibilityNodeData::IntListAttribute attribute, + std::vector<int32>* value) const; + + void SetStringAttribute( + AccessibilityNodeData::StringAttribute attribute, + const std::string& value); // Retrieve the value of a html attribute from the attribute map and // returns true if found. bool GetHtmlAttribute(const char* attr, string16* value) const; + bool GetHtmlAttribute(const char* attr, std::string* value) const; // Utility method to handle special cases for ARIA booleans, tristates and // booleans which have a "mixed" state. @@ -230,13 +232,13 @@ class CONTENT_EXPORT BrowserAccessibility { bool* is_mixed) const; // Returns true if the bit corresponding to the given state enum is 1. - bool HasState(AccessibilityNodeData::State state_enum) const; + bool HasState(WebKit::WebAXState state_enum) const; // Returns true if this node is an editable text field of any kind. bool IsEditableText() const; // Append the text from this node and its children. - string16 GetTextRecursive() const; + std::string GetTextRecursive() const; protected: // Perform platform specific initialization. This can be called multiple times @@ -264,21 +266,23 @@ class CONTENT_EXPORT BrowserAccessibility { std::vector<BrowserAccessibility*> children_; // Accessibility metadata from the renderer - string16 name_; - string16 value_; - BoolAttrMap bool_attributes_; - IntAttrMap int_attributes_; - FloatAttrMap float_attributes_; - StringAttrMap string_attributes_; - std::vector<std::pair<string16, string16> > html_attributes_; + std::string name_; + std::string value_; + std::vector<std::pair< + AccessibilityNodeData::BoolAttribute, bool> > bool_attributes_; + std::vector<std::pair< + AccessibilityNodeData::FloatAttribute, float> > float_attributes_; + std::vector<std::pair< + AccessibilityNodeData::IntAttribute, int> > int_attributes_; + std::vector<std::pair< + AccessibilityNodeData::StringAttribute, std::string> > string_attributes_; + std::vector<std::pair< + AccessibilityNodeData::IntListAttribute, std::vector<int32> > > + intlist_attributes_; + std::vector<std::pair<std::string, std::string> > html_attributes_; int32 role_; int32 state_; - string16 role_name_; gfx::Rect location_; - std::vector<int32> indirect_child_ids_; - std::vector<int32> line_breaks_; - std::vector<int32> cell_ids_; - std::vector<int32> unique_cell_ids_; // BrowserAccessibility objects are reference-counted on some platforms. // When we're done with this object and it's removed from our accessibility diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.cc b/chromium/content/browser/accessibility/browser_accessibility_android.cc index 8ff37ec0c7d..d8899d7d31c 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_android.cc @@ -30,8 +30,8 @@ bool BrowserAccessibilityAndroid::IsLeaf() const { // Iframes are always allowed to contain children. if (IsIframe() || - role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA || - role() == AccessibilityNodeData::ROLE_WEB_AREA) { + role() == WebKit::WebAXRoleRootWebArea || + role() == WebKit::WebAXRoleWebArea) { return false; } @@ -41,11 +41,11 @@ bool BrowserAccessibilityAndroid::IsLeaf() const { // Headings with text can drop their children. string16 name = GetText(); - if (role() == AccessibilityNodeData::ROLE_HEADING && !name.empty()) + if (role() == WebKit::WebAXRoleHeading && !name.empty()) return true; // Focusable nodes with text can drop their children. - if (HasState(AccessibilityNodeData::STATE_FOCUSABLE) && !name.empty()) + if (HasState(WebKit::WebAXStateFocusable) && !name.empty()) return true; // Nodes with only static text as children can drop their children. @@ -60,18 +60,18 @@ bool BrowserAccessibilityAndroid::IsCheckable() const { bool is_aria_pressed_defined; bool is_mixed; GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed); - if (role() == AccessibilityNodeData::ROLE_CHECKBOX || - role() == AccessibilityNodeData::ROLE_RADIO_BUTTON || + if (role() == WebKit::WebAXRoleCheckBox || + role() == WebKit::WebAXRoleRadioButton || is_aria_pressed_defined) { checkable = true; } - if (HasState(AccessibilityNodeData::STATE_CHECKED)) + if (HasState(WebKit::WebAXStateChecked)) checkable = true; return checkable; } bool BrowserAccessibilityAndroid::IsChecked() const { - return HasState(AccessibilityNodeData::STATE_CHECKED); + return HasState(WebKit::WebAXStateChecked); } bool BrowserAccessibilityAndroid::IsClickable() const { @@ -79,13 +79,13 @@ bool BrowserAccessibilityAndroid::IsClickable() const { } bool BrowserAccessibilityAndroid::IsEnabled() const { - return !HasState(AccessibilityNodeData::STATE_UNAVAILABLE); + return HasState(WebKit::WebAXStateEnabled); } bool BrowserAccessibilityAndroid::IsFocusable() const { - bool focusable = HasState(AccessibilityNodeData::STATE_FOCUSABLE); + bool focusable = HasState(WebKit::WebAXStateFocusable); if (IsIframe() || - role() == AccessibilityNodeData::ROLE_WEB_AREA) { + role() == WebKit::WebAXRoleWebArea) { focusable = false; } return focusable; @@ -96,7 +96,7 @@ bool BrowserAccessibilityAndroid::IsFocused() const { } bool BrowserAccessibilityAndroid::IsPassword() const { - return HasState(AccessibilityNodeData::STATE_PROTECTED); + return HasState(WebKit::WebAXStateProtected); } bool BrowserAccessibilityAndroid::IsScrollable() const { @@ -105,59 +105,59 @@ bool BrowserAccessibilityAndroid::IsScrollable() const { } bool BrowserAccessibilityAndroid::IsSelected() const { - return HasState(AccessibilityNodeData::STATE_SELECTED); + return HasState(WebKit::WebAXStateSelected); } bool BrowserAccessibilityAndroid::IsVisibleToUser() const { - return !HasState(AccessibilityNodeData::STATE_INVISIBLE); + return !HasState(WebKit::WebAXStateInvisible); } const char* BrowserAccessibilityAndroid::GetClassName() const { const char* class_name = NULL; switch(role()) { - case AccessibilityNodeData::ROLE_EDITABLE_TEXT: - case AccessibilityNodeData::ROLE_SPIN_BUTTON: - case AccessibilityNodeData::ROLE_TEXTAREA: - case AccessibilityNodeData::ROLE_TEXT_FIELD: + case WebKit::WebAXRoleEditableText: + case WebKit::WebAXRoleSpinButton: + case WebKit::WebAXRoleTextArea: + case WebKit::WebAXRoleTextField: class_name = "android.widget.EditText"; break; - case AccessibilityNodeData::ROLE_SLIDER: + case WebKit::WebAXRoleSlider: class_name = "android.widget.SeekBar"; break; - case AccessibilityNodeData::ROLE_COMBO_BOX: + case WebKit::WebAXRoleComboBox: class_name = "android.widget.Spinner"; break; - case AccessibilityNodeData::ROLE_BUTTON: - case AccessibilityNodeData::ROLE_MENU_BUTTON: - case AccessibilityNodeData::ROLE_POPUP_BUTTON: + case WebKit::WebAXRoleButton: + case WebKit::WebAXRoleMenuButton: + case WebKit::WebAXRolePopUpButton: class_name = "android.widget.Button"; break; - case AccessibilityNodeData::ROLE_CHECKBOX: + case WebKit::WebAXRoleCheckBox: class_name = "android.widget.CheckBox"; break; - case AccessibilityNodeData::ROLE_RADIO_BUTTON: + case WebKit::WebAXRoleRadioButton: class_name = "android.widget.RadioButton"; break; - case AccessibilityNodeData::ROLE_TOGGLE_BUTTON: + case WebKit::WebAXRoleToggleButton: class_name = "android.widget.ToggleButton"; break; - case AccessibilityNodeData::ROLE_CANVAS: - case AccessibilityNodeData::ROLE_IMAGE: + case WebKit::WebAXRoleCanvas: + case WebKit::WebAXRoleImage: class_name = "android.widget.Image"; break; - case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR: + case WebKit::WebAXRoleProgressIndicator: class_name = "android.widget.ProgressBar"; break; - case AccessibilityNodeData::ROLE_TAB_LIST: + case WebKit::WebAXRoleTabList: class_name = "android.widget.TabWidget"; break; - case AccessibilityNodeData::ROLE_GRID: - case AccessibilityNodeData::ROLE_TABLE: + case WebKit::WebAXRoleGrid: + case WebKit::WebAXRoleTable: class_name = "android.widget.GridView"; break; - case AccessibilityNodeData::ROLE_LIST: - case AccessibilityNodeData::ROLE_LISTBOX: + case WebKit::WebAXRoleList: + case WebKit::WebAXRoleListBox: class_name = "android.widget.ListView"; break; default: @@ -170,20 +170,19 @@ const char* BrowserAccessibilityAndroid::GetClassName() const { string16 BrowserAccessibilityAndroid::GetText() const { if (IsIframe() || - role() == AccessibilityNodeData::ROLE_WEB_AREA) { + role() == WebKit::WebAXRoleWebArea) { return string16(); } - string16 description; - GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description); - + string16 description = GetString16Attribute( + AccessibilityNodeData::ATTR_DESCRIPTION); string16 text; if (!name().empty()) - text = name(); + text = base::UTF8ToUTF16(name()); else if (!description.empty()) text = description; else if (!value().empty()) - text = value(); + text = base::UTF8ToUTF16(value()); if (text.empty() && HasOnlyStaticTextChildren()) { for (uint32 i = 0; i < child_count(); i++) { @@ -193,14 +192,13 @@ string16 BrowserAccessibilityAndroid::GetText() const { } switch(role()) { - case AccessibilityNodeData::ROLE_IMAGE_MAP_LINK: - case AccessibilityNodeData::ROLE_LINK: - case AccessibilityNodeData::ROLE_WEBCORE_LINK: + case WebKit::WebAXRoleImageMapLink: + case WebKit::WebAXRoleLink: if (!text.empty()) text += ASCIIToUTF16(" "); text += ASCIIToUTF16("Link"); break; - case AccessibilityNodeData::ROLE_HEADING: + case WebKit::WebAXRoleHeading: // Only append "heading" if this node already has text. if (!text.empty()) text += ASCIIToUTF16(" Heading"); @@ -213,12 +211,12 @@ string16 BrowserAccessibilityAndroid::GetText() const { int BrowserAccessibilityAndroid::GetItemIndex() const { int index = 0; switch(role()) { - case AccessibilityNodeData::ROLE_LIST_ITEM: - case AccessibilityNodeData::ROLE_LISTBOX_OPTION: + case WebKit::WebAXRoleListItem: + case WebKit::WebAXRoleListBoxOption: index = index_in_parent(); break; - case AccessibilityNodeData::ROLE_SLIDER: - case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR: { + case WebKit::WebAXRoleSlider: + case WebKit::WebAXRoleProgressIndicator: { float value_for_range; if (GetFloatAttribute( AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &value_for_range)) { @@ -233,12 +231,12 @@ int BrowserAccessibilityAndroid::GetItemIndex() const { int BrowserAccessibilityAndroid::GetItemCount() const { int count = 0; switch(role()) { - case AccessibilityNodeData::ROLE_LIST: - case AccessibilityNodeData::ROLE_LISTBOX: + case WebKit::WebAXRoleList: + case WebKit::WebAXRoleListBox: count = child_count(); break; - case AccessibilityNodeData::ROLE_SLIDER: - case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR: { + case WebKit::WebAXRoleSlider: + case WebKit::WebAXRoleProgressIndicator: { float max_value_for_range; if (GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE, &max_value_for_range)) { @@ -343,7 +341,7 @@ int BrowserAccessibilityAndroid::GetEditableTextLength() const { bool BrowserAccessibilityAndroid::HasFocusableChild() const { for (uint32 i = 0; i < child_count(); i++) { BrowserAccessibility* child = GetChild(i); - if (child->HasState(AccessibilityNodeData::STATE_FOCUSABLE)) + if (child->HasState(WebKit::WebAXStateFocusable)) return true; if (static_cast<BrowserAccessibilityAndroid*>(child)->HasFocusableChild()) return true; @@ -354,15 +352,15 @@ bool BrowserAccessibilityAndroid::HasFocusableChild() const { bool BrowserAccessibilityAndroid::HasOnlyStaticTextChildren() const { for (uint32 i = 0; i < child_count(); i++) { BrowserAccessibility* child = GetChild(i); - if (child->role() != AccessibilityNodeData::ROLE_STATIC_TEXT) + if (child->role() != WebKit::WebAXRoleStaticText) return false; } return true; } bool BrowserAccessibilityAndroid::IsIframe() const { - string16 html_tag; - GetStringAttribute(AccessibilityNodeData::ATTR_HTML_TAG, &html_tag); + string16 html_tag = GetString16Attribute( + AccessibilityNodeData::ATTR_HTML_TAG); return html_tag == ASCIIToUTF16("iframe"); } @@ -370,18 +368,18 @@ void BrowserAccessibilityAndroid::PostInitialize() { BrowserAccessibility::PostInitialize(); if (IsEditableText()) { - if (value_ != new_value_) { + if (base::UTF8ToUTF16(value_) != new_value_) { old_value_ = new_value_; - new_value_ = value_; + new_value_ = base::UTF8ToUTF16(value_); } } - if (role_ == AccessibilityNodeData::ROLE_ALERT && first_time_) - manager_->NotifyAccessibilityEvent(AccessibilityNotificationAlert, this); + if (role_ == WebKit::WebAXRoleAlert && first_time_) + manager_->NotifyAccessibilityEvent(WebKit::WebAXEventAlert, this); string16 live; - if (GetStringAttribute(AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS, - &live)) { + if (GetString16Attribute( + AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS, &live)) { NotifyLiveRegionUpdate(live); } @@ -396,7 +394,7 @@ void BrowserAccessibilityAndroid::NotifyLiveRegionUpdate(string16& aria_live) { string16 text = GetText(); if (cached_text_ != text) { if (!text.empty()) { - manager_->NotifyAccessibilityEvent(AccessibilityNotificationObjectShow, + manager_->NotifyAccessibilityEvent(WebKit::WebAXEventShow, this); } cached_text_ = text; diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h index a394822961c..d690d81dd4d 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h @@ -10,7 +10,7 @@ #import "base/mac/scoped_nsobject.h" #include "content/browser/accessibility/browser_accessibility.h" #import "content/browser/accessibility/browser_accessibility_delegate_mac.h" -#include "content/common/accessibility_node_data.h" +#include "third_party/WebKit/public/web/WebAXEnums.h" // BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility // object. The renderer converts webkit's accessibility tree into a @@ -40,7 +40,7 @@ // Convenience method to get the internal, cross-platform role // from browserAccessibility_. -- (content::AccessibilityNodeData::Role)internalRole; +- (WebKit::WebAXRole)internalRole; // Return the method name for the given attribute. For testing only. - (NSString*)methodNameForAttribute:(NSString*)attribute; diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm index 9d49767d266..be9ff5b329d 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm @@ -33,161 +33,136 @@ namespace { // Returns an autoreleased copy of the AccessibilityNodeData's attribute. NSString* NSStringForStringAttribute( - const std::map<StringAttribute, string16>& attributes, + BrowserAccessibility* browserAccessibility, StringAttribute attribute) { - std::map<StringAttribute, string16>::const_iterator iter = - attributes.find(attribute); - NSString* returnValue = @""; - if (iter != attributes.end()) { - returnValue = base::SysUTF16ToNSString(iter->second); - } - return returnValue; + return base::SysUTF8ToNSString( + browserAccessibility->GetStringAttribute(attribute)); } struct MapEntry { - AccessibilityNodeData::Role webKitValue; + WebKit::WebAXRole webKitValue; NSString* nativeValue; }; -typedef std::map<AccessibilityNodeData::Role, NSString*> RoleMap; +typedef std::map<WebKit::WebAXRole, NSString*> RoleMap; // GetState checks the bitmask used in AccessibilityNodeData to check // if the given state was set on the accessibility object. -bool GetState(BrowserAccessibility* accessibility, int state) { +bool GetState(BrowserAccessibility* accessibility, WebKit::WebAXState state) { return ((accessibility->state() >> state) & 1); } RoleMap BuildRoleMap() { const MapEntry roles[] = { - { AccessibilityNodeData::ROLE_ALERT, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_ALERT_DIALOG, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_ANNOTATION, NSAccessibilityUnknownRole }, - { AccessibilityNodeData::ROLE_APPLICATION, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_ARTICLE, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_BROWSER, NSAccessibilityBrowserRole }, - { AccessibilityNodeData::ROLE_BUSY_INDICATOR, - NSAccessibilityBusyIndicatorRole }, - { AccessibilityNodeData::ROLE_BUTTON, NSAccessibilityButtonRole }, - { AccessibilityNodeData::ROLE_CANVAS, NSAccessibilityImageRole }, - { AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_CELL, @"AXCell" }, - { AccessibilityNodeData::ROLE_CHECKBOX, NSAccessibilityCheckBoxRole }, - { AccessibilityNodeData::ROLE_COLOR_WELL, NSAccessibilityColorWellRole }, - { AccessibilityNodeData::ROLE_COMBO_BOX, NSAccessibilityComboBoxRole }, - { AccessibilityNodeData::ROLE_COLUMN, NSAccessibilityColumnRole }, - { AccessibilityNodeData::ROLE_COLUMN_HEADER, @"AXCell" }, - { AccessibilityNodeData::ROLE_DEFINITION, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_DETAIL, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_TERM, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_DIALOG, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_DIRECTORY, NSAccessibilityListRole }, - { AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE, - NSAccessibilityDisclosureTriangleRole }, - { AccessibilityNodeData::ROLE_DIV, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_DOCUMENT, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_DRAWER, NSAccessibilityDrawerRole }, - { AccessibilityNodeData::ROLE_EDITABLE_TEXT, NSAccessibilityTextFieldRole }, - { AccessibilityNodeData::ROLE_FOOTER, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_FORM, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_GRID, NSAccessibilityGridRole }, - { AccessibilityNodeData::ROLE_GROUP, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_GROW_AREA, NSAccessibilityGrowAreaRole }, - { AccessibilityNodeData::ROLE_HEADING, @"AXHeading" }, - { AccessibilityNodeData::ROLE_HELP_TAG, NSAccessibilityHelpTagRole }, - { AccessibilityNodeData::ROLE_HORIZONTAL_RULE, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_IGNORED, NSAccessibilityUnknownRole }, - { AccessibilityNodeData::ROLE_IMAGE, NSAccessibilityImageRole }, - { AccessibilityNodeData::ROLE_IMAGE_MAP, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole }, - { AccessibilityNodeData::ROLE_INCREMENTOR, NSAccessibilityIncrementorRole }, - { AccessibilityNodeData::ROLE_LABEL, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LANDMARK_APPLICATION, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LANDMARK_BANNER, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LANDMARK_MAIN, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LANDMARK_SEARCH, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LINK, NSAccessibilityLinkRole }, - { AccessibilityNodeData::ROLE_LIST, NSAccessibilityListRole }, - { AccessibilityNodeData::ROLE_LIST_ITEM, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_LIST_MARKER, @"AXListMarker" }, - { AccessibilityNodeData::ROLE_LISTBOX, NSAccessibilityListRole }, - { AccessibilityNodeData::ROLE_LISTBOX_OPTION, - NSAccessibilityStaticTextRole }, - { AccessibilityNodeData::ROLE_LOG, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_MARQUEE, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_MATH, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_MATTE, NSAccessibilityMatteRole }, - { AccessibilityNodeData::ROLE_MENU, NSAccessibilityMenuRole }, - { AccessibilityNodeData::ROLE_MENU_BAR, NSAccessibilityMenuBarRole }, - { AccessibilityNodeData::ROLE_MENU_ITEM, NSAccessibilityMenuItemRole }, - { AccessibilityNodeData::ROLE_MENU_BUTTON, NSAccessibilityButtonRole }, - { AccessibilityNodeData::ROLE_MENU_LIST_OPTION, - NSAccessibilityMenuItemRole }, - { AccessibilityNodeData::ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole }, - { AccessibilityNodeData::ROLE_NOTE, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_OUTLINE, NSAccessibilityOutlineRole }, - { AccessibilityNodeData::ROLE_PARAGRAPH, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_POPUP_BUTTON, - NSAccessibilityPopUpButtonRole }, - { AccessibilityNodeData::ROLE_PRESENTATIONAL, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_PROGRESS_INDICATOR, - NSAccessibilityProgressIndicatorRole }, - { AccessibilityNodeData::ROLE_RADIO_BUTTON, - NSAccessibilityRadioButtonRole }, - { AccessibilityNodeData::ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole }, - { AccessibilityNodeData::ROLE_REGION, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_ROOT_WEB_AREA, @"AXWebArea" }, - { AccessibilityNodeData::ROLE_ROW, NSAccessibilityRowRole }, - { AccessibilityNodeData::ROLE_ROW_HEADER, @"AXCell" }, - { AccessibilityNodeData::ROLE_RULER, NSAccessibilityRulerRole }, - { AccessibilityNodeData::ROLE_RULER_MARKER, - NSAccessibilityRulerMarkerRole }, + { WebKit::WebAXRoleAlert, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleAlertDialog, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleAnnotation, NSAccessibilityUnknownRole }, + { WebKit::WebAXRoleApplication, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleArticle, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleBrowser, NSAccessibilityBrowserRole }, + { WebKit::WebAXRoleBusyIndicator, NSAccessibilityBusyIndicatorRole }, + { WebKit::WebAXRoleButton, NSAccessibilityButtonRole }, + { WebKit::WebAXRoleCanvas, NSAccessibilityImageRole }, + { WebKit::WebAXRoleCell, @"AXCell" }, + { WebKit::WebAXRoleCheckBox, NSAccessibilityCheckBoxRole }, + { WebKit::WebAXRoleColorWell, NSAccessibilityColorWellRole }, + { WebKit::WebAXRoleComboBox, NSAccessibilityComboBoxRole }, + { WebKit::WebAXRoleColumn, NSAccessibilityColumnRole }, + { WebKit::WebAXRoleColumnHeader, @"AXCell" }, + { WebKit::WebAXRoleDefinition, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleDescriptionListDetail, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleDescriptionListTerm, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleDialog, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleDirectory, NSAccessibilityListRole }, + { WebKit::WebAXRoleDisclosureTriangle, + NSAccessibilityDisclosureTriangleRole }, + { WebKit::WebAXRoleDiv, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleDocument, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleDrawer, NSAccessibilityDrawerRole }, + { WebKit::WebAXRoleEditableText, NSAccessibilityTextFieldRole }, + { WebKit::WebAXRoleFooter, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleForm, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleGrid, NSAccessibilityGridRole }, + { WebKit::WebAXRoleGroup, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleGrowArea, NSAccessibilityGrowAreaRole }, + { WebKit::WebAXRoleHeading, @"AXHeading" }, + { WebKit::WebAXRoleHelpTag, NSAccessibilityHelpTagRole }, + { WebKit::WebAXRoleHorizontalRule, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleIgnored, NSAccessibilityUnknownRole }, + { WebKit::WebAXRoleImage, NSAccessibilityImageRole }, + { WebKit::WebAXRoleImageMap, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleImageMapLink, NSAccessibilityLinkRole }, + { WebKit::WebAXRoleIncrementor, NSAccessibilityIncrementorRole }, + { WebKit::WebAXRoleLabel, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleApplication, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleBanner, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleComplementary, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleContentInfo, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleMain, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleNavigation, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleSearch, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleLink, NSAccessibilityLinkRole }, + { WebKit::WebAXRoleList, NSAccessibilityListRole }, + { WebKit::WebAXRoleListItem, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleListMarker, @"AXListMarker" }, + { WebKit::WebAXRoleListBox, NSAccessibilityListRole }, + { WebKit::WebAXRoleListBoxOption, NSAccessibilityStaticTextRole }, + { WebKit::WebAXRoleLog, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleMarquee, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleMath, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleMatte, NSAccessibilityMatteRole }, + { WebKit::WebAXRoleMenu, NSAccessibilityMenuRole }, + { WebKit::WebAXRoleMenuBar, NSAccessibilityMenuBarRole }, + { WebKit::WebAXRoleMenuItem, NSAccessibilityMenuItemRole }, + { WebKit::WebAXRoleMenuButton, NSAccessibilityButtonRole }, + { WebKit::WebAXRoleMenuListOption, NSAccessibilityMenuItemRole }, + { WebKit::WebAXRoleMenuListPopup, NSAccessibilityUnknownRole }, + { WebKit::WebAXRoleNote, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleOutline, NSAccessibilityOutlineRole }, + { WebKit::WebAXRoleParagraph, NSAccessibilityGroupRole }, + { WebKit::WebAXRolePopUpButton, NSAccessibilityPopUpButtonRole }, + { WebKit::WebAXRolePresentational, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleProgressIndicator, + NSAccessibilityProgressIndicatorRole }, + { WebKit::WebAXRoleRadioButton, NSAccessibilityRadioButtonRole }, + { WebKit::WebAXRoleRadioGroup, NSAccessibilityRadioGroupRole }, + { WebKit::WebAXRoleRegion, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleRootWebArea, @"AXWebArea" }, + { WebKit::WebAXRoleRow, NSAccessibilityRowRole }, + { WebKit::WebAXRoleRowHeader, @"AXCell" }, + { WebKit::WebAXRoleRuler, NSAccessibilityRulerRole }, + { WebKit::WebAXRoleRulerMarker, NSAccessibilityRulerMarkerRole }, // TODO(dtseng): we don't correctly support the attributes for these roles. - // { AccessibilityNodeData::ROLE_SCROLLAREA, - // NSAccessibilityScrollAreaRole }, - { AccessibilityNodeData::ROLE_SCROLLBAR, NSAccessibilityScrollBarRole }, - { AccessibilityNodeData::ROLE_SHEET, NSAccessibilitySheetRole }, - { AccessibilityNodeData::ROLE_SLIDER, NSAccessibilitySliderRole }, - { AccessibilityNodeData::ROLE_SLIDER_THUMB, - NSAccessibilityValueIndicatorRole }, - { AccessibilityNodeData::ROLE_SPIN_BUTTON, NSAccessibilitySliderRole }, - { AccessibilityNodeData::ROLE_SPLITTER, NSAccessibilitySplitterRole }, - { AccessibilityNodeData::ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole }, - { AccessibilityNodeData::ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole }, - { AccessibilityNodeData::ROLE_STATUS, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_SVG_ROOT, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole }, - { AccessibilityNodeData::ROLE_TAB, NSAccessibilityRadioButtonRole }, - { AccessibilityNodeData::ROLE_TAB_LIST, NSAccessibilityTabGroupRole }, - { AccessibilityNodeData::ROLE_TAB_PANEL, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_TABLE, NSAccessibilityTableRole }, - { AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER, - NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED, - NSAccessibilityTabGroupRole }, - { AccessibilityNodeData::ROLE_TEXTAREA, NSAccessibilityTextAreaRole }, - { AccessibilityNodeData::ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole }, - { AccessibilityNodeData::ROLE_TIMER, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_TOGGLE_BUTTON, NSAccessibilityButtonRole }, - { AccessibilityNodeData::ROLE_TOOLBAR, NSAccessibilityToolbarRole }, - { AccessibilityNodeData::ROLE_TOOLTIP, NSAccessibilityGroupRole }, - { AccessibilityNodeData::ROLE_TREE, NSAccessibilityOutlineRole }, - { AccessibilityNodeData::ROLE_TREE_GRID, NSAccessibilityTableRole }, - { AccessibilityNodeData::ROLE_TREE_ITEM, NSAccessibilityRowRole }, - { AccessibilityNodeData::ROLE_VALUE_INDICATOR, - NSAccessibilityValueIndicatorRole }, - { AccessibilityNodeData::ROLE_WEBCORE_LINK, NSAccessibilityLinkRole }, - { AccessibilityNodeData::ROLE_WEB_AREA, @"AXWebArea" }, - { AccessibilityNodeData::ROLE_WINDOW, NSAccessibilityWindowRole }, + // { WebKit::WebAXRoleScrollArea, NSAccessibilityScrollAreaRole }, + { WebKit::WebAXRoleScrollBar, NSAccessibilityScrollBarRole }, + { WebKit::WebAXRoleSheet, NSAccessibilitySheetRole }, + { WebKit::WebAXRoleSlider, NSAccessibilitySliderRole }, + { WebKit::WebAXRoleSliderThumb, NSAccessibilityValueIndicatorRole }, + { WebKit::WebAXRoleSpinButton, NSAccessibilitySliderRole }, + { WebKit::WebAXRoleSplitter, NSAccessibilitySplitterRole }, + { WebKit::WebAXRoleSplitGroup, NSAccessibilitySplitGroupRole }, + { WebKit::WebAXRoleStaticText, NSAccessibilityStaticTextRole }, + { WebKit::WebAXRoleStatus, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleSVGRoot, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleSystemWide, NSAccessibilityUnknownRole }, + { WebKit::WebAXRoleTab, NSAccessibilityRadioButtonRole }, + { WebKit::WebAXRoleTabList, NSAccessibilityTabGroupRole }, + { WebKit::WebAXRoleTabPanel, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleTable, NSAccessibilityTableRole }, + { WebKit::WebAXRoleTableHeaderContainer, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleTextArea, NSAccessibilityTextAreaRole }, + { WebKit::WebAXRoleTextField, NSAccessibilityTextFieldRole }, + { WebKit::WebAXRoleTimer, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleToggleButton, NSAccessibilityButtonRole }, + { WebKit::WebAXRoleToolbar, NSAccessibilityToolbarRole }, + { WebKit::WebAXRoleUserInterfaceTooltip, NSAccessibilityGroupRole }, + { WebKit::WebAXRoleTree, NSAccessibilityOutlineRole }, + { WebKit::WebAXRoleTreeGrid, NSAccessibilityTableRole }, + { WebKit::WebAXRoleTreeItem, NSAccessibilityRowRole }, + { WebKit::WebAXRoleValueIndicator, NSAccessibilityValueIndicatorRole }, + { WebKit::WebAXRoleLink, NSAccessibilityLinkRole }, + { WebKit::WebAXRoleWebArea, @"AXWebArea" }, + { WebKit::WebAXRoleWindow, NSAccessibilityWindowRole }, }; RoleMap role_map; @@ -198,7 +173,7 @@ RoleMap BuildRoleMap() { // A mapping of webkit roles to native roles. NSString* NativeRoleFromAccessibilityNodeDataRole( - const AccessibilityNodeData::Role& role) { + const WebKit::WebAXRole& role) { CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role, (BuildRoleMap())); RoleMap::iterator it = web_accessibility_to_native_role.find(role); @@ -210,36 +185,32 @@ NSString* NativeRoleFromAccessibilityNodeDataRole( RoleMap BuildSubroleMap() { const MapEntry subroles[] = { - { AccessibilityNodeData::ROLE_ALERT, @"AXApplicationAlert" }, - { AccessibilityNodeData::ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog" }, - { AccessibilityNodeData::ROLE_ARTICLE, @"AXDocumentArticle" }, - { AccessibilityNodeData::ROLE_DEFINITION, @"AXDefinition" }, - { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_DETAIL, @"AXDescription" }, - { AccessibilityNodeData::ROLE_DESCRIPTION_LIST_TERM, @"AXTerm" }, - { AccessibilityNodeData::ROLE_DIALOG, @"AXApplicationDialog" }, - { AccessibilityNodeData::ROLE_DOCUMENT, @"AXDocument" }, - { AccessibilityNodeData::ROLE_FOOTER, @"AXLandmarkContentInfo" }, - { AccessibilityNodeData::ROLE_LANDMARK_APPLICATION, - @"AXLandmarkApplication" }, - { AccessibilityNodeData::ROLE_LANDMARK_BANNER, @"AXLandmarkBanner" }, - { AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY, - @"AXLandmarkComplementary" }, - { AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO, - @"AXLandmarkContentInfo" }, - { AccessibilityNodeData::ROLE_LANDMARK_MAIN, @"AXLandmarkMain" }, - { AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION, - @"AXLandmarkNavigation" }, - { AccessibilityNodeData::ROLE_LANDMARK_SEARCH, @"AXLandmarkSearch" }, - { AccessibilityNodeData::ROLE_LOG, @"AXApplicationLog" }, - { AccessibilityNodeData::ROLE_MARQUEE, @"AXApplicationMarquee" }, - { AccessibilityNodeData::ROLE_MATH, @"AXDocumentMath" }, - { AccessibilityNodeData::ROLE_NOTE, @"AXDocumentNote" }, - { AccessibilityNodeData::ROLE_REGION, @"AXDocumentRegion" }, - { AccessibilityNodeData::ROLE_STATUS, @"AXApplicationStatus" }, - { AccessibilityNodeData::ROLE_TAB_PANEL, @"AXTabPanel" }, - { AccessibilityNodeData::ROLE_TIMER, @"AXApplicationTimer" }, - { AccessibilityNodeData::ROLE_TOOLTIP, @"AXUserInterfaceTooltip" }, - { AccessibilityNodeData::ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole }, + { WebKit::WebAXRoleAlert, @"AXApplicationAlert" }, + { WebKit::WebAXRoleAlertDialog, @"AXApplicationAlertDialog" }, + { WebKit::WebAXRoleArticle, @"AXDocumentArticle" }, + { WebKit::WebAXRoleDefinition, @"AXDefinition" }, + { WebKit::WebAXRoleDescriptionListDetail, @"AXDescription" }, + { WebKit::WebAXRoleDescriptionListTerm, @"AXTerm" }, + { WebKit::WebAXRoleDialog, @"AXApplicationDialog" }, + { WebKit::WebAXRoleDocument, @"AXDocument" }, + { WebKit::WebAXRoleFooter, @"AXLandmarkContentInfo" }, + { WebKit::WebAXRoleApplication, @"AXLandmarkApplication" }, + { WebKit::WebAXRoleBanner, @"AXLandmarkBanner" }, + { WebKit::WebAXRoleComplementary, @"AXLandmarkComplementary" }, + { WebKit::WebAXRoleContentInfo, @"AXLandmarkContentInfo" }, + { WebKit::WebAXRoleMain, @"AXLandmarkMain" }, + { WebKit::WebAXRoleNavigation, @"AXLandmarkNavigation" }, + { WebKit::WebAXRoleSearch, @"AXLandmarkSearch" }, + { WebKit::WebAXRoleLog, @"AXApplicationLog" }, + { WebKit::WebAXRoleMarquee, @"AXApplicationMarquee" }, + { WebKit::WebAXRoleMath, @"AXDocumentMath" }, + { WebKit::WebAXRoleNote, @"AXDocumentNote" }, + { WebKit::WebAXRoleRegion, @"AXDocumentRegion" }, + { WebKit::WebAXRoleStatus, @"AXApplicationStatus" }, + { WebKit::WebAXRoleTabPanel, @"AXTabPanel" }, + { WebKit::WebAXRoleTimer, @"AXApplicationTimer" }, + { WebKit::WebAXRoleUserInterfaceTooltip, @"AXUserInterfaceTooltip" }, + { WebKit::WebAXRoleTreeItem, NSAccessibilityOutlineRowSubrole }, }; RoleMap subrole_map; @@ -250,7 +221,7 @@ RoleMap BuildSubroleMap() { // A mapping of webkit roles to native subroles. NSString* NativeSubroleFromAccessibilityNodeDataRole( - const AccessibilityNodeData::Role& role) { + const WebKit::WebAXRole& role) { CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole, (BuildSubroleMap())); RoleMap::iterator it = web_accessibility_to_native_subrole.find(role); @@ -353,34 +324,29 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSString*)accessKey { return NSStringForStringAttribute( - browserAccessibility_->string_attributes(), - AccessibilityNodeData::ATTR_ACCESS_KEY); + browserAccessibility_, AccessibilityNodeData::ATTR_ACCESS_KEY); } - (NSNumber*)ariaAtomic { - bool boolValue = false; - browserAccessibility_->GetBoolAttribute( - AccessibilityNodeData::ATTR_LIVE_ATOMIC, &boolValue); + bool boolValue = browserAccessibility_->GetBoolAttribute( + AccessibilityNodeData::ATTR_LIVE_ATOMIC); return [NSNumber numberWithBool:boolValue]; } - (NSNumber*)ariaBusy { - bool boolValue = false; - browserAccessibility_->GetBoolAttribute( - AccessibilityNodeData::ATTR_LIVE_BUSY, &boolValue); + bool boolValue = browserAccessibility_->GetBoolAttribute( + AccessibilityNodeData::ATTR_LIVE_BUSY); return [NSNumber numberWithBool:boolValue]; } - (NSString*)ariaLive { return NSStringForStringAttribute( - browserAccessibility_->string_attributes(), - AccessibilityNodeData::ATTR_LIVE_STATUS); + browserAccessibility_, AccessibilityNodeData::ATTR_LIVE_STATUS); } - (NSString*)ariaRelevant { return NSStringForStringAttribute( - browserAccessibility_->string_attributes(), - AccessibilityNodeData::ATTR_LIVE_RELEVANT); + browserAccessibility_, AccessibilityNodeData::ATTR_LIVE_RELEVANT); } // Returns an array of BrowserAccessibilityCocoa objects, representing the @@ -401,10 +367,11 @@ NSDictionary* attributeToMethodNameMap = nil; } // Also, add indirect children (if any). - for (uint32 i = 0; - i < browserAccessibility_->indirect_child_ids().size(); - ++i) { - int32 child_id = browserAccessibility_->indirect_child_ids()[i]; + const std::vector<int32>& indirectChildIds = + browserAccessibility_->GetIntListAttribute( + AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS); + for (uint32 i = 0; i < indirectChildIds.size(); ++i) { + int32 child_id = indirectChildIds[i]; BrowserAccessibility* child = browserAccessibility_->manager()->GetFromRendererID(child_id); @@ -430,26 +397,27 @@ NSDictionary* attributeToMethodNameMap = nil; } - (NSArray*)columnHeaders { - if ([self internalRole] != AccessibilityNodeData::ROLE_TABLE && - [self internalRole] != AccessibilityNodeData::ROLE_GRID) { + if ([self internalRole] != WebKit::WebAXRoleTable && + [self internalRole] != WebKit::WebAXRoleGrid) { return nil; } NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; const std::vector<int32>& uniqueCellIds = - browserAccessibility_->unique_cell_ids(); + browserAccessibility_->GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); for (size_t i = 0; i < uniqueCellIds.size(); ++i) { int id = uniqueCellIds[i]; BrowserAccessibility* cell = browserAccessibility_->manager()->GetFromRendererID(id); - if (cell && cell->role() == AccessibilityNodeData::ROLE_COLUMN_HEADER) + if (cell && cell->role() == WebKit::WebAXRoleColumnHeader) [ret addObject:cell->ToBrowserAccessibilityCocoa()]; } return ret; } - (NSValue*)columnIndexRange { - if ([self internalRole] != AccessibilityNodeData::ROLE_CELL) + if ([self internalRole] != WebKit::WebAXRoleCell) return nil; int column = -1; @@ -473,44 +441,43 @@ NSDictionary* attributeToMethodNameMap = nil; } - (NSString*)description { - const std::map<StringAttribute, string16>& attributes = - browserAccessibility_->string_attributes(); - std::map<StringAttribute, string16>::const_iterator iter = - attributes.find(AccessibilityNodeData::ATTR_DESCRIPTION); - if (iter != attributes.end()) - return base::SysUTF16ToNSString(iter->second); + std::string description; + if (browserAccessibility_->GetStringAttribute( + AccessibilityNodeData::ATTR_DESCRIPTION, &description)) { + return base::SysUTF8ToNSString(description); + } // If the role is anything other than an image, or if there's // a title or title UI element, just return an empty string. if (![[self role] isEqualToString:NSAccessibilityImageRole]) return @""; - if (!browserAccessibility_->name().empty()) + if (browserAccessibility_->HasStringAttribute( + AccessibilityNodeData::ATTR_NAME)) { return @""; + } if ([self titleUIElement]) return @""; // The remaining case is an image where there's no other title. // Return the base part of the filename as the description. - iter = attributes.find(AccessibilityNodeData::ATTR_URL); - if (iter != attributes.end()) { - string16 filename = iter->second; + std::string url; + if (browserAccessibility_->GetStringAttribute( + AccessibilityNodeData::ATTR_URL, &url)) { // Given a url like http://foo.com/bar/baz.png, just return the // base name, e.g., "baz.png". - size_t leftIndex = filename.size(); - while (leftIndex > 0 && filename[leftIndex - 1] != '/') - leftIndex--; - string16 basename = filename.substr(leftIndex); - - return base::SysUTF16ToNSString(basename); + size_t leftIndex = url.rfind('/'); + std::string basename = + leftIndex != std::string::npos ? url.substr(leftIndex) : url; + return base::SysUTF8ToNSString(basename); } return @""; } - (NSNumber*)disclosing { - if ([self internalRole] == AccessibilityNodeData::ROLE_TREE_ITEM) { + if ([self internalRole] == WebKit::WebAXRoleTreeItem) { return [NSNumber numberWithBool: - GetState(browserAccessibility_, AccessibilityNodeData::STATE_EXPANDED)]; + GetState(browserAccessibility_, WebKit::WebAXStateExpanded)]; } else { return nil; } @@ -523,12 +490,11 @@ NSDictionary* attributeToMethodNameMap = nil; } - (NSNumber*)disclosureLevel { - AccessibilityNodeData::Role role = [self internalRole]; - if (role == AccessibilityNodeData::ROLE_ROW || - role == AccessibilityNodeData::ROLE_TREE_ITEM) { - int level = 0; - browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, &level); + WebKit::WebAXRole role = [self internalRole]; + if (role == WebKit::WebAXRoleRow || + role == WebKit::WebAXRoleTreeItem) { + int level = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL); // Mac disclosureLevel is 0-based, but web levels are 1-based. if (level > 0) level--; @@ -545,8 +511,7 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSNumber*)enabled { return [NSNumber numberWithBool: - !GetState(browserAccessibility_, - AccessibilityNodeData::STATE_UNAVAILABLE)]; + GetState(browserAccessibility_, WebKit::WebAXStateEnabled)]; } - (NSNumber*)focused { @@ -558,14 +523,14 @@ NSDictionary* attributeToMethodNameMap = nil; - (id)header { int headerElementId = -1; - if ([self internalRole] == AccessibilityNodeData::ROLE_TABLE || - [self internalRole] == AccessibilityNodeData::ROLE_GRID) { + if ([self internalRole] == WebKit::WebAXRoleTable || + [self internalRole] == WebKit::WebAXRoleGrid) { browserAccessibility_->GetIntAttribute( AccessibilityNodeData::ATTR_TABLE_HEADER_ID, &headerElementId); - } else if ([self internalRole] == AccessibilityNodeData::ROLE_COLUMN) { + } else if ([self internalRole] == WebKit::WebAXRoleColumn) { browserAccessibility_->GetIntAttribute( AccessibilityNodeData::ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId); - } else if ([self internalRole] == AccessibilityNodeData::ROLE_ROW) { + } else if ([self internalRole] == WebKit::WebAXRoleRow) { browserAccessibility_->GetIntAttribute( AccessibilityNodeData::ATTR_TABLE_ROW_HEADER_ID, &headerElementId); } @@ -581,23 +546,18 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSString*)help { return NSStringForStringAttribute( - browserAccessibility_->string_attributes(), - AccessibilityNodeData::ATTR_HELP); + browserAccessibility_, AccessibilityNodeData::ATTR_HELP); } - (NSNumber*)index { - if ([self internalRole] == AccessibilityNodeData::ROLE_COLUMN) { - int columnIndex; - if (browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_TABLE_COLUMN_INDEX, &columnIndex)) { - return [NSNumber numberWithInt:columnIndex]; - } - } else if ([self internalRole] == AccessibilityNodeData::ROLE_ROW) { - int rowIndex; - if (browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_TABLE_ROW_INDEX, &rowIndex)) { - return [NSNumber numberWithInt:rowIndex]; - } + if ([self internalRole] == WebKit::WebAXRoleColumn) { + int columnIndex = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_TABLE_COLUMN_INDEX); + return [NSNumber numberWithInt:columnIndex]; + } else if ([self internalRole] == WebKit::WebAXRoleRow) { + int rowIndex = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_TABLE_ROW_INDEX); + return [NSNumber numberWithInt:rowIndex]; } return nil; @@ -626,33 +586,30 @@ NSDictionary* attributeToMethodNameMap = nil; } - (NSNumber*)loadingProgress { - float floatValue = 0.0; - browserAccessibility_->GetFloatAttribute( - AccessibilityNodeData::ATTR_DOC_LOADING_PROGRESS, &floatValue); + float floatValue = browserAccessibility_->GetFloatAttribute( + AccessibilityNodeData::ATTR_DOC_LOADING_PROGRESS); return [NSNumber numberWithFloat:floatValue]; } - (NSNumber*)maxValue { - float floatValue = 0.0; - browserAccessibility_->GetFloatAttribute( - AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE, &floatValue); + float floatValue = browserAccessibility_->GetFloatAttribute( + AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE); return [NSNumber numberWithFloat:floatValue]; } - (NSNumber*)minValue { - float floatValue = 0.0; - browserAccessibility_->GetFloatAttribute( - AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE, &floatValue); + float floatValue = browserAccessibility_->GetFloatAttribute( + AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE); return [NSNumber numberWithFloat:floatValue]; } - (NSString*)orientation { // We present a spin button as a vertical slider, with a role description // of "spin button". - if ([self internalRole] == AccessibilityNodeData::ROLE_SPIN_BUTTON) + if ([self internalRole] == WebKit::WebAXRoleSpinButton) return NSAccessibilityVerticalOrientationValue; - if (GetState(browserAccessibility_, AccessibilityNodeData::STATE_VERTICAL)) + if (GetState(browserAccessibility_, WebKit::WebAXStateVertical)) return NSAccessibilityVerticalOrientationValue; else return NSAccessibilityHorizontalOrientationValue; @@ -690,18 +647,23 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSNumber*)required { return [NSNumber numberWithBool: - GetState(browserAccessibility_, AccessibilityNodeData::STATE_REQUIRED)]; + GetState(browserAccessibility_, WebKit::WebAXStateRequired)]; } // Returns an enum indicating the role from browserAccessibility_. -- (AccessibilityNodeData::Role)internalRole { - return static_cast<AccessibilityNodeData::Role>( - browserAccessibility_->role()); +- (WebKit::WebAXRole)internalRole { + return static_cast<WebKit::WebAXRole>(browserAccessibility_->role()); } // Returns a string indicating the NSAccessibility role of this object. - (NSString*)role { - return NativeRoleFromAccessibilityNodeDataRole([self internalRole]); + WebKit::WebAXRole role = [self internalRole]; + if (role == WebKit::WebAXRoleCanvas && + browserAccessibility_->GetBoolAttribute( + AccessibilityNodeData::ATTR_CANVAS_HAS_FALLBACK)) { + return NSAccessibilityGroupRole; + } + return NativeRoleFromAccessibilityNodeDataRole(role); } // Returns a string indicating the role description of this object. @@ -728,27 +690,23 @@ NSDictionary* attributeToMethodNameMap = nil; if ([role isEqualToString:NSAccessibilityGroupRole] || [role isEqualToString:NSAccessibilityRadioButtonRole]) { - const std::vector<std::pair<string16, string16> >& htmlAttributes = - browserAccessibility_->html_attributes(); - AccessibilityNodeData::Role browserAccessibilityRole = [self internalRole]; - if ((browserAccessibilityRole != AccessibilityNodeData::ROLE_GROUP && - browserAccessibilityRole != AccessibilityNodeData::ROLE_LIST_ITEM) || - browserAccessibilityRole == AccessibilityNodeData::ROLE_TAB) { - for (size_t i = 0; i < htmlAttributes.size(); ++i) { - const std::pair<string16, string16>& htmlAttribute = htmlAttributes[i]; - if (htmlAttribute.first == ASCIIToUTF16("role")) { - // TODO(dtseng): This is not localized; see crbug/84814. - return base::SysUTF16ToNSString(htmlAttribute.second); - } + std::string role; + if (browserAccessibility_->GetHtmlAttribute("role", &role)) { + WebKit::WebAXRole internalRole = [self internalRole]; + if ((internalRole != WebKit::WebAXRoleGroup && + internalRole != WebKit::WebAXRoleListItem) || + internalRole == WebKit::WebAXRoleTab) { + // TODO(dtseng): This is not localized; see crbug/84814. + return base::SysUTF8ToNSString(role); } } } switch([self internalRole]) { - case AccessibilityNodeData::ROLE_FOOTER: + case WebKit::WebAXRoleFooter: return base::SysUTF16ToNSString(content_client->GetLocalizedString( IDS_AX_ROLE_FOOTER)); - case AccessibilityNodeData::ROLE_SPIN_BUTTON: + case WebKit::WebAXRoleSpinButton: // This control is similar to what VoiceOver calls a "stepper". return base::SysUTF16ToNSString(content_client->GetLocalizedString( IDS_AX_ROLE_STEPPER)); @@ -760,26 +718,27 @@ NSDictionary* attributeToMethodNameMap = nil; } - (NSArray*)rowHeaders { - if ([self internalRole] != AccessibilityNodeData::ROLE_TABLE && - [self internalRole] != AccessibilityNodeData::ROLE_GRID) { + if ([self internalRole] != WebKit::WebAXRoleTable && + [self internalRole] != WebKit::WebAXRoleGrid) { return nil; } NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; const std::vector<int32>& uniqueCellIds = - browserAccessibility_->unique_cell_ids(); + browserAccessibility_->GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); for (size_t i = 0; i < uniqueCellIds.size(); ++i) { int id = uniqueCellIds[i]; BrowserAccessibility* cell = browserAccessibility_->manager()->GetFromRendererID(id); - if (cell && cell->role() == AccessibilityNodeData::ROLE_ROW_HEADER) + if (cell && cell->role() == WebKit::WebAXRoleRowHeader) [ret addObject:cell->ToBrowserAccessibilityCocoa()]; } return ret; } - (NSValue*)rowIndexRange { - if ([self internalRole] != AccessibilityNodeData::ROLE_CELL) + if ([self internalRole] != WebKit::WebAXRoleCell) return nil; int row = -1; @@ -796,15 +755,16 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSArray*)rows { NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; - if ([self internalRole] == AccessibilityNodeData::ROLE_TABLE|| - [self internalRole] == AccessibilityNodeData::ROLE_GRID) { + if ([self internalRole] == WebKit::WebAXRoleTable|| + [self internalRole] == WebKit::WebAXRoleGrid) { for (BrowserAccessibilityCocoa* child in [self children]) { if ([[child role] isEqualToString:NSAccessibilityRowRole]) [ret addObject:child]; } - } else if ([self internalRole] == AccessibilityNodeData::ROLE_COLUMN) { + } else if ([self internalRole] == WebKit::WebAXRoleColumn) { const std::vector<int32>& indirectChildIds = - browserAccessibility_->indirect_child_ids(); + browserAccessibility_->GetIntListAttribute( + AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS); for (uint32 i = 0; i < indirectChildIds.size(); ++i) { int id = indirectChildIds[i]; BrowserAccessibility* rowElement = @@ -825,17 +785,16 @@ NSDictionary* attributeToMethodNameMap = nil; // Returns a subrole based upon the role. - (NSString*) subrole { - AccessibilityNodeData::Role browserAccessibilityRole = [self internalRole]; - if (browserAccessibilityRole == AccessibilityNodeData::ROLE_TEXT_FIELD && - GetState(browserAccessibility_, AccessibilityNodeData::STATE_PROTECTED)) { + WebKit::WebAXRole browserAccessibilityRole = [self internalRole]; + if (browserAccessibilityRole == WebKit::WebAXRoleTextField && + GetState(browserAccessibility_, WebKit::WebAXStateProtected)) { return @"AXSecureTextField"; } NSString* htmlTag = NSStringForStringAttribute( - browserAccessibility_->string_attributes(), - AccessibilityNodeData::ATTR_HTML_TAG); + browserAccessibility_, AccessibilityNodeData::ATTR_HTML_TAG); - if (browserAccessibilityRole == AccessibilityNodeData::ROLE_LIST) { + if (browserAccessibilityRole == WebKit::WebAXRoleList) { if ([htmlTag isEqualToString:@"ul"] || [htmlTag isEqualToString:@"ol"]) { return @"AXContentList"; @@ -851,7 +810,7 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSArray*)tabs { NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease]; - if ([self internalRole] == AccessibilityNodeData::ROLE_TAB) + if ([self internalRole] == WebKit::WebAXRoleTab) [tabSubtree addObject:self]; for (uint i=0; i < [[self children] count]; ++i) { @@ -864,7 +823,8 @@ NSDictionary* attributeToMethodNameMap = nil; } - (NSString*)title { - return base::SysUTF16ToNSString(browserAccessibility_->name()); + return NSStringForStringAttribute( + browserAccessibility_, AccessibilityNodeData::ATTR_NAME); } - (id)titleUIElement { @@ -884,9 +844,7 @@ NSDictionary* attributeToMethodNameMap = nil; [[self role] isEqualToString:@"AXWebArea"] ? AccessibilityNodeData::ATTR_DOC_URL : AccessibilityNodeData::ATTR_URL; - return NSStringForStringAttribute( - browserAccessibility_->string_attributes(), - urlAttribute); + return NSStringForStringAttribute(browserAccessibility_, urlAttribute); } - (id)value { @@ -895,7 +853,7 @@ NSDictionary* attributeToMethodNameMap = nil; // to approximate Cocoa ax behavior best as we can. NSString* role = [self role]; if ([role isEqualToString:@"AXHeading"]) { - int level; + int level = 0; if (browserAccessibility_->GetIntAttribute( AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, &level)) { return [NSNumber numberWithInt:level]; @@ -907,17 +865,16 @@ NSDictionary* attributeToMethodNameMap = nil; [role isEqualToString:NSAccessibilityRadioButtonRole]) { int value = 0; value = GetState( - browserAccessibility_, AccessibilityNodeData::STATE_CHECKED) ? 1 : 0; + browserAccessibility_, WebKit::WebAXStateChecked) ? 1 : 0; value = GetState( - browserAccessibility_, AccessibilityNodeData::STATE_SELECTED) ? + browserAccessibility_, WebKit::WebAXStateSelected) ? 1 : value; - bool mixed = false; - browserAccessibility_->GetBoolAttribute( - AccessibilityNodeData::ATTR_BUTTON_MIXED, &mixed); - if (mixed) + if (browserAccessibility_->GetBoolAttribute( + AccessibilityNodeData::ATTR_BUTTON_MIXED)) { value = 2; + } return [NSNumber numberWithInt:value]; } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] || [role isEqualToString:NSAccessibilitySliderRole] || @@ -928,26 +885,24 @@ NSDictionary* attributeToMethodNameMap = nil; return [NSNumber numberWithFloat:floatValue]; } } else if ([role isEqualToString:NSAccessibilityColorWellRole]) { - int r, g, b; - browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_COLOR_VALUE_RED, &r); - browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN, &g); - browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE, &b); + int r = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_COLOR_VALUE_RED); + int g = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN); + int b = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE); // This string matches the one returned by a native Mac color well. return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1", r / 255., g / 255., b / 255.]; } - return base::SysUTF16ToNSString(browserAccessibility_->value()); + return NSStringForStringAttribute( + browserAccessibility_, AccessibilityNodeData::ATTR_VALUE); } - (NSString*)valueDescription { - if (!browserAccessibility_->value().empty()) - return base::SysUTF16ToNSString(browserAccessibility_->value()); - else - return nil; + return NSStringForStringAttribute( + browserAccessibility_, AccessibilityNodeData::ATTR_VALUE); } - (NSValue*)visibleCharacterRange { @@ -958,7 +913,8 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSArray*)visibleCells { NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; const std::vector<int32>& uniqueCellIds = - browserAccessibility_->unique_cell_ids(); + browserAccessibility_->GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); for (size_t i = 0; i < uniqueCellIds.size(); ++i) { int id = uniqueCellIds[i]; BrowserAccessibility* cell = @@ -979,7 +935,7 @@ NSDictionary* attributeToMethodNameMap = nil; - (NSNumber*)visited { return [NSNumber numberWithBool: - GetState(browserAccessibility_, AccessibilityNodeData::STATE_TRAVERSED)]; + GetState(browserAccessibility_, WebKit::WebAXStateVisited)]; } - (id)window { @@ -1013,7 +969,8 @@ NSDictionary* attributeToMethodNameMap = nil; if ([attribute isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) { const std::vector<int32>& line_breaks = - browserAccessibility_->line_breaks(); + browserAccessibility_->GetIntListAttribute( + AccessibilityNodeData::ATTR_LINE_BREAKS); for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) { if (line_breaks[i] > selStart) return [NSNumber numberWithInt:i]; @@ -1021,8 +978,9 @@ NSDictionary* attributeToMethodNameMap = nil; return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())]; } if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { - return base::SysUTF16ToNSString(browserAccessibility_->value().substr( - selStart, selLength)); + std::string value = browserAccessibility_->GetStringAttribute( + AccessibilityNodeData::ATTR_VALUE); + return base::SysUTF8ToNSString(value.substr(selStart, selLength)); } if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { return [NSValue valueWithRange:NSMakeRange(selStart, selLength)]; @@ -1038,14 +996,17 @@ NSDictionary* attributeToMethodNameMap = nil; if (!browserAccessibility_) return nil; - const std::vector<int32>& line_breaks = browserAccessibility_->line_breaks(); + const std::vector<int32>& line_breaks = + browserAccessibility_->GetIntListAttribute( + AccessibilityNodeData::ATTR_LINE_BREAKS); int len = static_cast<int>(browserAccessibility_->value().size()); if ([attribute isEqualToString: NSAccessibilityStringForRangeParameterizedAttribute]) { NSRange range = [(NSValue*)parameter rangeValue]; - return base::SysUTF16ToNSString( - browserAccessibility_->value().substr(range.location, range.length)); + std::string value = browserAccessibility_->GetStringAttribute( + AccessibilityNodeData::ATTR_VALUE); + return base::SysUTF8ToNSString(value.substr(range.location, range.length)); } if ([attribute isEqualToString: @@ -1072,8 +1033,8 @@ NSDictionary* attributeToMethodNameMap = nil; if ([attribute isEqualToString: NSAccessibilityCellForColumnAndRowParameterizedAttribute]) { - if ([self internalRole] != AccessibilityNodeData::ROLE_TABLE && - [self internalRole] != AccessibilityNodeData::ROLE_GRID) { + if ([self internalRole] != WebKit::WebAXRoleTable && + [self internalRole] != WebKit::WebAXRoleGrid) { return nil; } if (![parameter isKindOfClass:[NSArray self]]) @@ -1081,12 +1042,10 @@ NSDictionary* attributeToMethodNameMap = nil; NSArray* array = parameter; int column = [[array objectAtIndex:0] intValue]; int row = [[array objectAtIndex:1] intValue]; - int num_columns = 0; - int num_rows = 0; - browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &num_columns); - browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &num_rows); + int num_columns = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT); + int num_rows = browserAccessibility_->GetIntAttribute( + AccessibilityNodeData::ATTR_TABLE_ROW_COUNT); if (column < 0 || column >= num_columns || row < 0 || row >= num_rows) { return nil; @@ -1095,7 +1054,7 @@ NSDictionary* attributeToMethodNameMap = nil; i < browserAccessibility_->child_count(); ++i) { BrowserAccessibility* child = browserAccessibility_->GetChild(i); - if (child->role() != AccessibilityNodeData::ROLE_ROW) + if (child->role() != WebKit::WebAXRoleRow) continue; int rowIndex; if (!child->GetIntAttribute( @@ -1110,7 +1069,7 @@ NSDictionary* attributeToMethodNameMap = nil; j < child->child_count(); ++j) { BrowserAccessibility* cell = child->GetChild(j); - if (cell->role() != AccessibilityNodeData::ROLE_CELL) + if (cell->role() != WebKit::WebAXRoleCell) continue; int colIndex; if (!cell->GetIntAttribute( @@ -1335,16 +1294,15 @@ NSDictionary* attributeToMethodNameMap = nil; } // Live regions. - string16 s; - if (browserAccessibility_->GetStringAttribute( - AccessibilityNodeData::ATTR_LIVE_STATUS, &s)) { + if (browserAccessibility_->HasStringAttribute( + AccessibilityNodeData::ATTR_LIVE_STATUS)) { [ret addObjectsFromArray:[NSArray arrayWithObjects: @"AXARIALive", @"AXARIARelevant", nil]]; } - if (browserAccessibility_->GetStringAttribute( - AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS, &s)) { + if (browserAccessibility_->HasStringAttribute( + AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS)) { [ret addObjectsFromArray:[NSArray arrayWithObjects: @"AXARIAAtomic", @"AXARIABusy", @@ -1352,9 +1310,8 @@ NSDictionary* attributeToMethodNameMap = nil; } // Title UI Element. - int i; - if (browserAccessibility_->GetIntAttribute( - AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &i)) { + if (browserAccessibility_->HasIntAttribute( + AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT)) { [ret addObjectsFromArray:[NSArray arrayWithObjects: NSAccessibilityTitleUIElementAttribute, nil]]; @@ -1385,12 +1342,10 @@ NSDictionary* attributeToMethodNameMap = nil; if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) return GetState(browserAccessibility_, - AccessibilityNodeData::STATE_FOCUSABLE); + WebKit::WebAXStateFocusable); if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { - bool canSetValue = false; - browserAccessibility_->GetBoolAttribute( - AccessibilityNodeData::ATTR_CAN_SET_VALUE, &canSetValue); - return canSetValue; + return browserAccessibility_->GetBoolAttribute( + AccessibilityNodeData::ATTR_CAN_SET_VALUE); } if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] && ([[self role] isEqualToString:NSAccessibilityTextFieldRole] || diff --git a/chromium/content/browser/accessibility/browser_accessibility_gtk.cc b/chromium/content/browser/accessibility/browser_accessibility_gtk.cc index 50364282b3d..faf4c82e307 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_gtk.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_gtk.cc @@ -178,7 +178,8 @@ static const gchar* browser_accessibility_get_name(AtkObject* atk_object) { BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); if (!obj) return NULL; - return obj->atk_acc_name().c_str(); + + return obj->GetStringAttribute(AccessibilityNodeData::ATTR_NAME).c_str(); } static const gchar* browser_accessibility_get_description( @@ -186,7 +187,9 @@ static const gchar* browser_accessibility_get_description( BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); if (!obj) return NULL; - return obj->atk_acc_description().c_str(); + + return obj->GetStringAttribute( + AccessibilityNodeData::ATTR_DESCRIPTION).c_str(); } static AtkObject* browser_accessibility_get_parent(AtkObject* atk_object) { @@ -247,11 +250,11 @@ static AtkStateSet* browser_accessibility_ref_state_set(AtkObject* atk_object) { ref_state_set(atk_object); int32 state = obj->state(); - if (state & (1 << AccessibilityNodeData::STATE_FOCUSABLE)) + if (state & (1 << WebKit::WebAXStateFocusable)) atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE); if (obj->manager()->GetFocus(NULL) == obj) atk_state_set_add_state(state_set, ATK_STATE_FOCUSED); - if (!(state & (1 << AccessibilityNodeData::STATE_UNAVAILABLE))) + if (state & (1 << WebKit::WebAXStateEnabled)) atk_state_set_add_state(state_set, ATK_STATE_ENABLED); return state_set; @@ -356,9 +359,9 @@ static int GetInterfaceMaskFromObject(BrowserAccessibilityGtk* obj) { interface_mask |= 1 << ATK_COMPONENT_INTERFACE; int role = obj->role(); - if (role == AccessibilityNodeData::ROLE_PROGRESS_INDICATOR || - role == AccessibilityNodeData::ROLE_SCROLLBAR || - role == AccessibilityNodeData::ROLE_SLIDER) { + if (role == WebKit::WebAXRoleProgressIndicator || + role == WebKit::WebAXRoleScrollBar || + role == WebKit::WebAXRoleSlider) { interface_mask |= 1 << ATK_VALUE_INTERFACE; } @@ -467,49 +470,40 @@ bool BrowserAccessibilityGtk::IsNative() const { } void BrowserAccessibilityGtk::InitRoleAndState() { - atk_acc_name_ = UTF16ToUTF8(name()); - - string16 description; - GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description); - atk_acc_description_ = UTF16ToUTF8(description); - switch(role_) { - case AccessibilityNodeData::ROLE_DOCUMENT: - case AccessibilityNodeData::ROLE_ROOT_WEB_AREA: - case AccessibilityNodeData::ROLE_WEB_AREA: + case WebKit::WebAXRoleDocument: + case WebKit::WebAXRoleRootWebArea: + case WebKit::WebAXRoleWebArea: atk_role_ = ATK_ROLE_DOCUMENT_WEB; break; - case AccessibilityNodeData::ROLE_GROUP: - case AccessibilityNodeData::ROLE_DIV: + case WebKit::WebAXRoleGroup: + case WebKit::WebAXRoleDiv: atk_role_ = ATK_ROLE_SECTION; break; - case AccessibilityNodeData::ROLE_BUTTON: + case WebKit::WebAXRoleButton: atk_role_ = ATK_ROLE_PUSH_BUTTON; break; - case AccessibilityNodeData::ROLE_CHECKBOX: + case WebKit::WebAXRoleCheckBox: atk_role_ = ATK_ROLE_CHECK_BOX; break; - case AccessibilityNodeData::ROLE_COMBO_BOX: + case WebKit::WebAXRoleComboBox: atk_role_ = ATK_ROLE_COMBO_BOX; break; - case AccessibilityNodeData::ROLE_LINK: + case WebKit::WebAXRoleLink: atk_role_ = ATK_ROLE_LINK; break; - case AccessibilityNodeData::ROLE_RADIO_BUTTON: + case WebKit::WebAXRoleRadioButton: atk_role_ = ATK_ROLE_RADIO_BUTTON; break; - case AccessibilityNodeData::ROLE_STATIC_TEXT: + case WebKit::WebAXRoleStaticText: atk_role_ = ATK_ROLE_TEXT; break; - case AccessibilityNodeData::ROLE_TEXTAREA: + case WebKit::WebAXRoleTextArea: atk_role_ = ATK_ROLE_ENTRY; break; - case AccessibilityNodeData::ROLE_TEXT_FIELD: + case WebKit::WebAXRoleTextField: atk_role_ = ATK_ROLE_ENTRY; break; - case AccessibilityNodeData::ROLE_WEBCORE_LINK: - atk_role_ = ATK_ROLE_LINK; - break; default: atk_role_ = ATK_ROLE_UNKNOWN; break; diff --git a/chromium/content/browser/accessibility/browser_accessibility_gtk.h b/chromium/content/browser/accessibility/browser_accessibility_gtk.h index 8f3a2e31e5b..c8895e358d1 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_gtk.h +++ b/chromium/content/browser/accessibility/browser_accessibility_gtk.h @@ -68,8 +68,6 @@ class BrowserAccessibilityGtk : public BrowserAccessibility { AtkObject* GetAtkObject() const; AtkRole atk_role() { return atk_role_; } - const std::string& atk_acc_name() { return atk_acc_name_; } - const std::string& atk_acc_description() { return atk_acc_description_; } // BrowserAccessibility methods. virtual void PreInitialize() OVERRIDE; @@ -83,8 +81,6 @@ class BrowserAccessibilityGtk : public BrowserAccessibility { AtkObject* atk_object_; AtkRole atk_role_; - std::string atk_acc_name_; - std::string atk_acc_description_; int interface_mask_; private: diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm index 45137ec677a..d23d8e6a691 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm @@ -67,25 +67,24 @@ class BrowserAccessibilityTest : public ui::CocoaTest { root.id = 1000; root.location.set_width(500); root.location.set_height(100); - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; - root.string_attributes[AccessibilityNodeData::ATTR_HELP] = - ASCIIToUTF16("HelpText"); + root.role = WebKit::WebAXRoleRootWebArea; + root.AddStringAttribute(AccessibilityNodeData::ATTR_HELP, "HelpText"); root.child_ids.push_back(1001); root.child_ids.push_back(1002); AccessibilityNodeData child1; child1.id = 1001; - child1.name = ASCIIToUTF16("Child1"); + child1.SetName("Child1"); child1.location.set_width(250); child1.location.set_height(100); - child1.role = AccessibilityNodeData::ROLE_BUTTON; + child1.role = WebKit::WebAXRoleButton; AccessibilityNodeData child2; child2.id = 1002; child2.location.set_x(250); child2.location.set_width(250); child2.location.set_height(100); - child2.role = AccessibilityNodeData::ROLE_HEADING; + child2.role = WebKit::WebAXRoleHeading; delegate_.reset([[MockAccessibilityDelegate alloc] init]); manager_.reset( diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.cc b/chromium/content/browser/accessibility/browser_accessibility_manager.cc index 7def57459f9..2161fb11243 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.cc @@ -69,7 +69,7 @@ void BrowserAccessibilityManager::Initialize(const AccessibilityNodeData src) { AccessibilityNodeData BrowserAccessibilityManager::GetEmptyDocument() { AccessibilityNodeData empty_document; empty_document.id = 0; - empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + empty_document.role = WebKit::WebAXRoleRootWebArea; return empty_document; } @@ -93,7 +93,7 @@ void BrowserAccessibilityManager::GotFocus(bool touch_event_context) { if (!focus_) return; - NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_); + NotifyAccessibilityEvent(WebKit::WebAXEventFocus, focus_); } void BrowserAccessibilityManager::WasHidden() { @@ -102,7 +102,7 @@ void BrowserAccessibilityManager::WasHidden() { void BrowserAccessibilityManager::GotMouseDown() { osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT; - NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_); + NotifyAccessibilityEvent(WebKit::WebAXEventFocus, focus_); } bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) { @@ -124,24 +124,24 @@ void BrowserAccessibilityManager::RemoveNode(BrowserAccessibility* node) { renderer_id_map_.erase(renderer_id); } -void BrowserAccessibilityManager::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void BrowserAccessibilityManager::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { for (uint32 index = 0; index < params.size(); index++) { - const AccessibilityHostMsg_NotificationParams& param = params[index]; + const AccessibilityHostMsg_EventParams& param = params[index]; // Update nodes that changed. if (!UpdateNodes(param.nodes)) return; // Find the node corresponding to the id that's the target of the - // notification (which may not be the root of the update tree). + // event (which may not be the root of the update tree). BrowserAccessibility* node = GetFromRendererID(param.id); if (!node) continue; - int notification_type = param.notification_type; - if (notification_type == AccessibilityNotificationFocusChanged || - notification_type == AccessibilityNotificationBlur) { + WebKit::WebAXEvent event_type = param.event_type; + if (event_type == WebKit::WebAXEventFocus || + event_type == WebKit::WebAXEventBlur) { SetFocus(node, false); if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN && @@ -154,15 +154,15 @@ void BrowserAccessibilityManager::OnAccessibilityNotifications( continue; } - // Send the notification event to the operating system. - NotifyAccessibilityEvent(notification_type, node); + // Send the event event to the operating system. + NotifyAccessibilityEvent(event_type, node); // Set initial focus when a page is loaded. - if (notification_type == AccessibilityNotificationLoadComplete) { + if (event_type == WebKit::WebAXEventLoadComplete) { if (!focus_) SetFocus(root_, false); if (!delegate_ || delegate_->HasFocus()) - NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_); + NotifyAccessibilityEvent(WebKit::WebAXEventFocus, focus_); } } } @@ -333,16 +333,18 @@ bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) { // and this is a serious error. BrowserAccessibility* instance = GetFromRendererID(src.id); if (!instance) { - if (src.role != AccessibilityNodeData::ROLE_ROOT_WEB_AREA) + if (src.role != WebKit::WebAXRoleRootWebArea) return false; instance = CreateNode(NULL, src.id, 0); } - if (src.bool_attributes.find( - AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) != - src.bool_attributes.end()) { - instance->SetLocation(src.location); - return true; + // TODO(dmazzoni): avoid a linear scan here. + for (size_t i = 0; i < src.bool_attributes.size(); i++) { + if (src.bool_attributes[i].first == + AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) { + instance->SetLocation(src.location); + return true; + } } // Update all of the node-specific data, like its role, state, name, etc. @@ -398,7 +400,7 @@ bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) { instance->SwapChildren(new_children); // Handle the case where this node is the new root of the tree. - if (src.role == AccessibilityNodeData::ROLE_ROOT_WEB_AREA && + if (src.role == WebKit::WebAXRoleRootWebArea && (!root_ || root_->renderer_id() != src.id)) { if (root_) root_->Destroy(); @@ -408,9 +410,9 @@ bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) { } // Keep track of what node is focused. - if (src.role != AccessibilityNodeData::ROLE_ROOT_WEB_AREA && - src.role != AccessibilityNodeData::ROLE_WEB_AREA && - (src.state >> AccessibilityNodeData::STATE_FOCUSED & 1)) { + if (src.role != WebKit::WebAXRoleRootWebArea && + src.role != WebKit::WebAXRoleWebArea && + (src.state >> WebKit::WebAXStateFocused & 1)) { SetFocus(instance, false); } return success; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.h b/chromium/content/browser/accessibility/browser_accessibility_manager.h index 5fac3e5e3f3..09439b2a4b5 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.h @@ -12,9 +12,10 @@ #include "build/build_config.h" #include "content/common/accessibility_node_data.h" #include "content/common/content_export.h" +#include "third_party/WebKit/public/web/WebAXEnums.h" #include "ui/gfx/native_widget_types.h" -struct AccessibilityHostMsg_NotificationParams; +struct AccessibilityHostMsg_EventParams; namespace content { class BrowserAccessibility; @@ -65,12 +66,8 @@ class CONTENT_EXPORT BrowserAccessibilityManager { static AccessibilityNodeData GetEmptyDocument(); - // Type is enum AccessibilityNotification. - // We pass it as int so that we don't include the message declaration - // header here. virtual void NotifyAccessibilityEvent( - int type, - BrowserAccessibility* node) { } + WebKit::WebAXEvent event_type, BrowserAccessibility* node) { } // Return a pointer to the root of the tree, does not make a new reference. BrowserAccessibility* GetRoot(); @@ -126,8 +123,8 @@ class CONTENT_EXPORT BrowserAccessibilityManager { // Called when the renderer process has notified us of about tree changes. // Send a notification to MSAA clients of the change. - void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params); + void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params); #if defined(OS_WIN) BrowserAccessibilityManagerWin* ToBrowserAccessibilityManagerWin(); @@ -146,7 +143,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager { virtual bool UseRootScrollOffsetsWhenComputingBounds(); // For testing only: update the given nodes as if they were - // received from the renderer process in OnAccessibilityNotifications. + // received from the renderer process in OnAccessibilityEvents. // Takes up to 7 nodes at once so tests don't need to create a vector // each time. void UpdateNodesForTesting( diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc index 2da686af934..2943c5cf4be 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc @@ -77,13 +77,13 @@ BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() { AccessibilityNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() { AccessibilityNodeData empty_document; empty_document.id = 0; - empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; - empty_document.state = 1 << AccessibilityNodeData::STATE_READONLY; + empty_document.role = WebKit::WebAXRoleRootWebArea; + empty_document.state = 1 << WebKit::WebAXStateReadonly; return empty_document; } void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( - int type, + WebKit::WebAXEvent event_type, BrowserAccessibility* node) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); @@ -96,27 +96,27 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( Java_BrowserAccessibilityManager_handleContentChanged( env, obj.obj(), node->renderer_id()); - switch (type) { - case AccessibilityNotificationLoadComplete: + switch (event_type) { + case WebKit::WebAXEventLoadComplete: Java_BrowserAccessibilityManager_handlePageLoaded( env, obj.obj(), focus_->renderer_id()); break; - case AccessibilityNotificationFocusChanged: + case WebKit::WebAXEventFocus: Java_BrowserAccessibilityManager_handleFocusChanged( env, obj.obj(), node->renderer_id()); break; - case AccessibilityNotificationCheckStateChanged: + case WebKit::WebAXEventCheckedStateChanged: Java_BrowserAccessibilityManager_handleCheckStateChanged( env, obj.obj(), node->renderer_id()); break; - case AccessibilityNotificationScrolledToAnchor: + case WebKit::WebAXEventScrolledToAnchor: Java_BrowserAccessibilityManager_handleScrolledToAnchor( env, obj.obj(), node->renderer_id()); break; - case AccessibilityNotificationAlert: + case WebKit::WebAXEventAlert: // An alert is a special case of live region. Fall through to the // next case to handle it. - case AccessibilityNotificationObjectShow: { + case WebKit::WebAXEventShow: { // This event is fired when an object appears in a live region. // Speak its text. BrowserAccessibilityAndroid* android_node = @@ -127,13 +127,13 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( env, android_node->GetText()).obj()); break; } - case AccessibilityNotificationSelectedTextChanged: + case WebKit::WebAXEventSelectedTextChanged: Java_BrowserAccessibilityManager_handleTextSelectionChanged( env, obj.obj(), node->renderer_id()); break; - case AccessibilityNotificationChildrenChanged: - case AccessibilityNotificationTextChanged: - case AccessibilityNotificationValueChanged: + case WebKit::WebAXEventChildrenChanged: + case WebKit::WebAXEventTextChanged: + case WebKit::WebAXEventValueChanged: if (node->IsEditableText()) { Java_BrowserAccessibilityManager_handleEditableTextChanged( env, obj.obj(), node->renderer_id()); diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.h b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h index 2a6c291bacb..056fe8dff6f 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_android.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h @@ -30,8 +30,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid static AccessibilityNodeData GetEmptyDocument(); // Implementation of BrowserAccessibilityManager. - virtual void NotifyAccessibilityEvent(int type, - BrowserAccessibility* node) OVERRIDE; + virtual void NotifyAccessibilityEvent( + WebKit::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE; // -------------------------------------------------------------------------- // Methods called from Java via JNI diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc index 15191c6a35b..a6246c5e795 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.cc @@ -38,24 +38,24 @@ BrowserAccessibilityManagerGtk::~BrowserAccessibilityManagerGtk() { AccessibilityNodeData BrowserAccessibilityManagerGtk::GetEmptyDocument() { AccessibilityNodeData empty_document; empty_document.id = 0; - empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + empty_document.role = WebKit::WebAXRoleRootWebArea; empty_document.state = - 1 << AccessibilityNodeData::STATE_READONLY; + 1 << WebKit::WebAXStateReadonly; return empty_document; } void BrowserAccessibilityManagerGtk::NotifyAccessibilityEvent( - int type, + WebKit::WebAXEvent event_type, BrowserAccessibility* node) { if (!node->IsNative()) return; AtkObject* atk_object = node->ToBrowserAccessibilityGtk()->GetAtkObject(); - switch (type) { - case AccessibilityNotificationChildrenChanged: + switch (event_type) { + case WebKit::WebAXEventChildrenChanged: RecursivelySendChildrenChanged(GetRoot()->ToBrowserAccessibilityGtk()); break; - case AccessibilityNotificationFocusChanged: + case WebKit::WebAXEventFocus: // Note: atk_focus_tracker_notify may be deprecated in the future; // follow this bug for the replacement: // https://bugzilla.gnome.org/show_bug.cgi?id=649575#c4 diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h index eedae7ca121..4c29d8d7955 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_gtk.h @@ -27,8 +27,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerGtk static AccessibilityNodeData GetEmptyDocument(); // BrowserAccessibilityManager methods - virtual void NotifyAccessibilityEvent(int type, BrowserAccessibility* node) - OVERRIDE; + virtual void NotifyAccessibilityEvent( + WebKit::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE; GtkWidget* parent_widget() { return parent_widget_; } diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h index d94c2c73df8..70ca4192a38 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h @@ -23,8 +23,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac static AccessibilityNodeData GetEmptyDocument(); // Implementation of BrowserAccessibilityManager. - virtual void NotifyAccessibilityEvent(int type, - BrowserAccessibility* node) OVERRIDE; + virtual void NotifyAccessibilityEvent( + WebKit::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE; NSView* parent_view() { return parent_view_; } diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm index e3280997a25..1abf5fc5611 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm @@ -31,87 +31,108 @@ BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac( AccessibilityNodeData BrowserAccessibilityManagerMac::GetEmptyDocument() { AccessibilityNodeData empty_document; empty_document.id = 0; - empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + empty_document.role = WebKit::WebAXRoleRootWebArea; empty_document.state = - 1 << AccessibilityNodeData::STATE_READONLY; + 1 << WebKit::WebAXStateReadonly; return empty_document; } void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( - int type, + WebKit::WebAXEvent event_type, BrowserAccessibility* node) { if (!node->IsNative()) return; // Refer to AXObjectCache.mm (webkit). NSString* event_id = @""; - switch (type) { - case AccessibilityNotificationActiveDescendantChanged: - if (node->role() == AccessibilityNodeData::ROLE_TREE) + switch (event_type) { + case WebKit::WebAXEventActiveDescendantChanged: + if (node->role() == WebKit::WebAXRoleTree) event_id = NSAccessibilitySelectedRowsChangedNotification; else event_id = NSAccessibilityFocusedUIElementChangedNotification; break; - case AccessibilityNotificationAlert: + case WebKit::WebAXEventAlert: // Not used on Mac. return; - case AccessibilityNotificationBlur: + case WebKit::WebAXEventBlur: // A no-op on Mac. return; - case AccessibilityNotificationCheckStateChanged: + case WebKit::WebAXEventCheckedStateChanged: // Not used on Mac. return; - case AccessibilityNotificationChildrenChanged: + case WebKit::WebAXEventChildrenChanged: // TODO(dtseng): no clear equivalent on Mac. return; - case AccessibilityNotificationFocusChanged: + case WebKit::WebAXEventFocus: event_id = NSAccessibilityFocusedUIElementChangedNotification; break; - case AccessibilityNotificationLayoutComplete: + case WebKit::WebAXEventLayoutComplete: event_id = @"AXLayoutComplete"; break; - case AccessibilityNotificationLiveRegionChanged: + case WebKit::WebAXEventLiveRegionChanged: event_id = @"AXLiveRegionChanged"; break; - case AccessibilityNotificationLoadComplete: + case WebKit::WebAXEventLoadComplete: event_id = @"AXLoadComplete"; break; - case AccessibilityNotificationMenuListValueChanged: + case WebKit::WebAXEventMenuListValueChanged: // Not used on Mac. return; - case AccessibilityNotificationObjectShow: + case WebKit::WebAXEventShow: // Not used on Mac. return; - case AccessibilityNotificationObjectHide: + case WebKit::WebAXEventHide: // Not used on Mac. return; - case AccessibilityNotificationRowCountChanged: + case WebKit::WebAXEventRowCountChanged: event_id = NSAccessibilityRowCountChangedNotification; break; - case AccessibilityNotificationRowCollapsed: + case WebKit::WebAXEventRowCollapsed: event_id = @"AXRowCollapsed"; break; - case AccessibilityNotificationRowExpanded: + case WebKit::WebAXEventRowExpanded: event_id = @"AXRowExpanded"; break; - case AccessibilityNotificationScrolledToAnchor: + case WebKit::WebAXEventScrolledToAnchor: // Not used on Mac. return; - case AccessibilityNotificationSelectedChildrenChanged: + case WebKit::WebAXEventSelectedChildrenChanged: event_id = NSAccessibilitySelectedChildrenChangedNotification; break; - case AccessibilityNotificationSelectedTextChanged: + case WebKit::WebAXEventSelectedTextChanged: event_id = NSAccessibilitySelectedTextChangedNotification; break; - case AccessibilityNotificationTextInserted: + case WebKit::WebAXEventTextInserted: // Not used on Mac. return; - case AccessibilityNotificationTextRemoved: + case WebKit::WebAXEventTextRemoved: // Not used on Mac. return; - case AccessibilityNotificationValueChanged: + case WebKit::WebAXEventValueChanged: event_id = NSAccessibilityValueChangedNotification; break; + case WebKit::WebAXEventAriaAttributeChanged: + // Not used on Mac. + return; + case WebKit::WebAXEventAutocorrectionOccured: + // Not used on Mac. + return; + case WebKit::WebAXEventInvalidStatusChanged: + // Not used on Mac. + return; + case WebKit::WebAXEventLocationChanged: + // Not used on Mac. + return; + case WebKit::WebAXEventMenuListItemSelected: + // Not used on Mac. + return; + case WebKit::WebAXEventTextChanged: + // Not used on Mac. + return; + default: + LOG(WARNING) << "Unknown accessibility event: " << event_type; + return; } BrowserAccessibilityCocoa* native_node = node->ToBrowserAccessibilityCocoa(); DCHECK(native_node); diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc index 5cb03cd25ce..d6a9f554c8f 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc @@ -92,20 +92,20 @@ TEST(BrowserAccessibilityManagerTest, TestNoLeaks) { // BrowserAccessibilityManager. AccessibilityNodeData button; button.id = 2; - button.name = UTF8ToUTF16("Button"); - button.role = AccessibilityNodeData::ROLE_BUTTON; + button.SetName("Button"); + button.role = WebKit::WebAXRoleButton; button.state = 0; AccessibilityNodeData checkbox; checkbox.id = 3; - checkbox.name = UTF8ToUTF16("Checkbox"); - checkbox.role = AccessibilityNodeData::ROLE_CHECKBOX; + checkbox.SetName("Checkbox"); + checkbox.role = WebKit::WebAXRoleCheckBox; checkbox.state = 0; AccessibilityNodeData root; root.id = 1; - root.name = UTF8ToUTF16("Document"); - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + root.SetName("Document"); + root.role = WebKit::WebAXRoleRootWebArea; root.state = 0; root.child_ids.push_back(2); root.child_ids.push_back(3); @@ -170,26 +170,26 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) { AccessibilityNodeData tree1_child1; tree1_child1.id = 2; - tree1_child1.name = UTF8ToUTF16("Child1"); - tree1_child1.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_child1.SetName("Child1"); + tree1_child1.role = WebKit::WebAXRoleButton; tree1_child1.state = 0; AccessibilityNodeData tree1_child2; tree1_child2.id = 3; - tree1_child2.name = UTF8ToUTF16("Child2"); - tree1_child2.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_child2.SetName("Child2"); + tree1_child2.role = WebKit::WebAXRoleButton; tree1_child2.state = 0; AccessibilityNodeData tree1_child3; tree1_child3.id = 4; - tree1_child3.name = UTF8ToUTF16("Child3"); - tree1_child3.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_child3.SetName("Child3"); + tree1_child3.role = WebKit::WebAXRoleButton; tree1_child3.state = 0; AccessibilityNodeData tree1_root; tree1_root.id = 1; - tree1_root.name = UTF8ToUTF16("Document"); - tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + tree1_root.SetName("Document"); + tree1_root.role = WebKit::WebAXRoleRootWebArea; tree1_root.state = 0; tree1_root.child_ids.push_back(2); tree1_root.child_ids.push_back(3); @@ -205,14 +205,14 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) { AccessibilityNodeData tree2_child0; tree2_child0.id = 5; - tree2_child0.name = UTF8ToUTF16("Child0"); - tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON; + tree2_child0.SetName("Child0"); + tree2_child0.role = WebKit::WebAXRoleButton; tree2_child0.state = 0; AccessibilityNodeData tree2_root; tree2_root.id = 1; - tree2_root.name = UTF8ToUTF16("DocumentChanged"); - tree2_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + tree2_root.SetName("DocumentChanged"); + tree2_root.role = WebKit::WebAXRoleRootWebArea; tree2_root.state = 0; tree2_root.child_ids.push_back(5); tree2_root.child_ids.push_back(2); @@ -248,14 +248,14 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) { EXPECT_EQ(2, child3_accessible->index_in_parent()); // Process a notification containing the changed subtree. - std::vector<AccessibilityHostMsg_NotificationParams> params; - params.push_back(AccessibilityHostMsg_NotificationParams()); - AccessibilityHostMsg_NotificationParams* msg = ¶ms[0]; - msg->notification_type = AccessibilityNotificationChildrenChanged; + std::vector<AccessibilityHostMsg_EventParams> params; + params.push_back(AccessibilityHostMsg_EventParams()); + AccessibilityHostMsg_EventParams* msg = ¶ms[0]; + msg->event_type = WebKit::WebAXEventChildrenChanged; msg->nodes.push_back(tree2_root); msg->nodes.push_back(tree2_child0); msg->id = tree2_root.id; - manager->OnAccessibilityNotifications(params); + manager->OnAccessibilityEvents(params); // There should be 5 objects now: the 4 from the new tree, plus the // reference to child3 we kept. @@ -302,47 +302,47 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) { AccessibilityNodeData tree1_grandchild1; tree1_grandchild1.id = 4; - tree1_grandchild1.name = UTF8ToUTF16("GrandChild1"); - tree1_grandchild1.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_grandchild1.SetName("GrandChild1"); + tree1_grandchild1.role = WebKit::WebAXRoleButton; tree1_grandchild1.state = 0; AccessibilityNodeData tree1_child1; tree1_child1.id = 3; - tree1_child1.name = UTF8ToUTF16("Child1"); - tree1_child1.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_child1.SetName("Child1"); + tree1_child1.role = WebKit::WebAXRoleButton; tree1_child1.state = 0; tree1_child1.child_ids.push_back(4); AccessibilityNodeData tree1_grandchild2; tree1_grandchild2.id = 6; - tree1_grandchild2.name = UTF8ToUTF16("GrandChild1"); - tree1_grandchild2.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_grandchild2.SetName("GrandChild1"); + tree1_grandchild2.role = WebKit::WebAXRoleButton; tree1_grandchild2.state = 0; AccessibilityNodeData tree1_child2; tree1_child2.id = 5; - tree1_child2.name = UTF8ToUTF16("Child2"); - tree1_child2.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_child2.SetName("Child2"); + tree1_child2.role = WebKit::WebAXRoleButton; tree1_child2.state = 0; tree1_child2.child_ids.push_back(6); AccessibilityNodeData tree1_grandchild3; tree1_grandchild3.id = 8; - tree1_grandchild3.name = UTF8ToUTF16("GrandChild3"); - tree1_grandchild3.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_grandchild3.SetName("GrandChild3"); + tree1_grandchild3.role = WebKit::WebAXRoleButton; tree1_grandchild3.state = 0; AccessibilityNodeData tree1_child3; tree1_child3.id = 7; - tree1_child3.name = UTF8ToUTF16("Child3"); - tree1_child3.role = AccessibilityNodeData::ROLE_BUTTON; + tree1_child3.SetName("Child3"); + tree1_child3.role = WebKit::WebAXRoleButton; tree1_child3.state = 0; tree1_child3.child_ids.push_back(8); AccessibilityNodeData tree1_container; tree1_container.id = 2; - tree1_container.name = UTF8ToUTF16("Container"); - tree1_container.role = AccessibilityNodeData::ROLE_GROUP; + tree1_container.SetName("Container"); + tree1_container.role = WebKit::WebAXRoleGroup; tree1_container.state = 0; tree1_container.child_ids.push_back(3); tree1_container.child_ids.push_back(5); @@ -350,8 +350,8 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) { AccessibilityNodeData tree1_root; tree1_root.id = 1; - tree1_root.name = UTF8ToUTF16("Document"); - tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + tree1_root.SetName("Document"); + tree1_root.role = WebKit::WebAXRoleRootWebArea; tree1_root.state = 0; tree1_root.child_ids.push_back(2); @@ -369,21 +369,21 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) { AccessibilityNodeData tree2_grandchild0; tree2_grandchild0.id = 9; - tree2_grandchild0.name = UTF8ToUTF16("GrandChild0"); - tree2_grandchild0.role = AccessibilityNodeData::ROLE_BUTTON; + tree2_grandchild0.SetName("GrandChild0"); + tree2_grandchild0.role = WebKit::WebAXRoleButton; tree2_grandchild0.state = 0; AccessibilityNodeData tree2_child0; tree2_child0.id = 10; - tree2_child0.name = UTF8ToUTF16("Child0"); - tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON; + tree2_child0.SetName("Child0"); + tree2_child0.role = WebKit::WebAXRoleButton; tree2_child0.state = 0; tree2_child0.child_ids.push_back(9); AccessibilityNodeData tree2_container; tree2_container.id = 2; - tree2_container.name = UTF8ToUTF16("Container"); - tree2_container.role = AccessibilityNodeData::ROLE_GROUP; + tree2_container.SetName("Container"); + tree2_container.role = WebKit::WebAXRoleGroup; tree2_container.state = 0; tree2_container.child_ids.push_back(10); tree2_container.child_ids.push_back(3); @@ -424,15 +424,15 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) { // Process a notification containing the changed subtree rooted at // the container. - std::vector<AccessibilityHostMsg_NotificationParams> params; - params.push_back(AccessibilityHostMsg_NotificationParams()); - AccessibilityHostMsg_NotificationParams* msg = ¶ms[0]; - msg->notification_type = AccessibilityNotificationChildrenChanged; + std::vector<AccessibilityHostMsg_EventParams> params; + params.push_back(AccessibilityHostMsg_EventParams()); + AccessibilityHostMsg_EventParams* msg = ¶ms[0]; + msg->event_type = WebKit::WebAXEventChildrenChanged; msg->nodes.push_back(tree2_container); msg->nodes.push_back(tree2_child0); msg->nodes.push_back(tree2_grandchild0); msg->id = tree2_container.id; - manager->OnAccessibilityNotifications(params); + manager->OnAccessibilityEvents(params); // There should be 9 objects now: the 8 from the new tree, plus the // reference to child3 we kept. @@ -489,7 +489,7 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) { AccessibilityNodeData tree1_1; tree1_1.id = 1; - tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + tree1_1.role = WebKit::WebAXRoleRootWebArea; tree1_1.state = 0; tree1_1.child_ids.push_back(2); tree1_1.child_ids.push_back(3); @@ -531,16 +531,16 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) { ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_); // Process a notification containing the changed subtree. - std::vector<AccessibilityHostMsg_NotificationParams> params; - params.push_back(AccessibilityHostMsg_NotificationParams()); - AccessibilityHostMsg_NotificationParams* msg = ¶ms[0]; - msg->notification_type = AccessibilityNotificationChildrenChanged; + std::vector<AccessibilityHostMsg_EventParams> params; + params.push_back(AccessibilityHostMsg_EventParams()); + AccessibilityHostMsg_EventParams* msg = ¶ms[0]; + msg->event_type = WebKit::WebAXEventChildrenChanged; msg->nodes.push_back(tree2_1); msg->nodes.push_back(tree2_4); msg->nodes.push_back(tree2_5); msg->nodes.push_back(tree2_6); msg->id = tree2_1.id; - manager->OnAccessibilityNotifications(params); + manager->OnAccessibilityEvents(params); // There should be 4 objects now. EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_); @@ -557,7 +557,7 @@ TEST(BrowserAccessibilityManagerTest, TestFatalError) { AccessibilityNodeData root; root.id = 1; - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + root.role = WebKit::WebAXRoleRootWebArea; root.child_ids.push_back(2); root.child_ids.push_back(2); @@ -575,7 +575,7 @@ TEST(BrowserAccessibilityManagerTest, TestFatalError) { AccessibilityNodeData root2; root2.id = 1; - root2.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + root2.role = WebKit::WebAXRoleRootWebArea; root2.child_ids.push_back(2); root2.child_ids.push_back(3); diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc index b956d218d33..881d1b80b25 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc @@ -46,10 +46,11 @@ BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() { AccessibilityNodeData BrowserAccessibilityManagerWin::GetEmptyDocument() { AccessibilityNodeData empty_document; empty_document.id = 0; - empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + empty_document.role = WebKit::WebAXRoleRootWebArea; empty_document.state = - (1 << AccessibilityNodeData::STATE_READONLY) | - (1 << AccessibilityNodeData::STATE_BUSY); + (1 << WebKit::WebAXStateEnabled) | + (1 << WebKit::WebAXStateReadonly) | + (1 << WebKit::WebAXStateBusy); return empty_document; } @@ -76,79 +77,79 @@ void BrowserAccessibilityManagerWin::RemoveNode(BrowserAccessibility* node) { } void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( - int type, + WebKit::WebAXEvent event_type, BrowserAccessibility* node) { LONG event_id = EVENT_MIN; - switch (type) { - case AccessibilityNotificationActiveDescendantChanged: + switch (event_type) { + case WebKit::WebAXEventActiveDescendantChanged: event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; break; - case AccessibilityNotificationAlert: + case WebKit::WebAXEventAlert: event_id = EVENT_SYSTEM_ALERT; break; - case AccessibilityNotificationAriaAttributeChanged: + case WebKit::WebAXEventAriaAttributeChanged: event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; break; - case AccessibilityNotificationAutocorrectionOccurred: + case WebKit::WebAXEventAutocorrectionOccured: event_id = IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED; break; - case AccessibilityNotificationBlur: + case WebKit::WebAXEventBlur: // Equivalent to focus on the root. event_id = EVENT_OBJECT_FOCUS; node = GetRoot(); break; - case AccessibilityNotificationCheckStateChanged: + case WebKit::WebAXEventCheckedStateChanged: event_id = EVENT_OBJECT_STATECHANGE; break; - case AccessibilityNotificationChildrenChanged: + case WebKit::WebAXEventChildrenChanged: event_id = EVENT_OBJECT_REORDER; break; - case AccessibilityNotificationFocusChanged: + case WebKit::WebAXEventFocus: event_id = EVENT_OBJECT_FOCUS; break; - case AccessibilityNotificationInvalidStatusChanged: + case WebKit::WebAXEventInvalidStatusChanged: event_id = EVENT_OBJECT_STATECHANGE; break; - case AccessibilityNotificationLiveRegionChanged: + case WebKit::WebAXEventLiveRegionChanged: // TODO: try not firing a native notification at all, since // on Windows, each individual item in a live region that changes // already gets its own notification. event_id = EVENT_OBJECT_REORDER; break; - case AccessibilityNotificationLoadComplete: + case WebKit::WebAXEventLoadComplete: event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE; break; - case AccessibilityNotificationMenuListItemSelected: + case WebKit::WebAXEventMenuListItemSelected: event_id = EVENT_OBJECT_FOCUS; break; - case AccessibilityNotificationMenuListValueChanged: + case WebKit::WebAXEventMenuListValueChanged: event_id = EVENT_OBJECT_VALUECHANGE; break; - case AccessibilityNotificationObjectHide: + case WebKit::WebAXEventHide: event_id = EVENT_OBJECT_HIDE; break; - case AccessibilityNotificationObjectShow: + case WebKit::WebAXEventShow: event_id = EVENT_OBJECT_SHOW; break; - case AccessibilityNotificationScrolledToAnchor: + case WebKit::WebAXEventScrolledToAnchor: event_id = EVENT_SYSTEM_SCROLLINGSTART; break; - case AccessibilityNotificationSelectedChildrenChanged: + case WebKit::WebAXEventSelectedChildrenChanged: event_id = EVENT_OBJECT_SELECTIONWITHIN; break; - case AccessibilityNotificationSelectedTextChanged: + case WebKit::WebAXEventSelectedTextChanged: event_id = IA2_EVENT_TEXT_CARET_MOVED; break; - case AccessibilityNotificationTextChanged: + case WebKit::WebAXEventTextChanged: event_id = EVENT_OBJECT_NAMECHANGE; break; - case AccessibilityNotificationTextInserted: + case WebKit::WebAXEventTextInserted: event_id = IA2_EVENT_TEXT_INSERTED; break; - case AccessibilityNotificationTextRemoved: + case WebKit::WebAXEventTextRemoved: event_id = IA2_EVENT_TEXT_REMOVED; break; - case AccessibilityNotificationValueChanged: + case WebKit::WebAXEventValueChanged: event_id = EVENT_OBJECT_VALUECHANGE; break; default: @@ -169,7 +170,7 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( // If this is a layout complete notification (sent when a container scrolls) // and there is a descendant tracked object, send a notification on it. // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. - if (type == AccessibilityNotificationLayoutComplete && + if (event_type == WebKit::WebAXEventLayoutComplete && tracked_scroll_object_ && tracked_scroll_object_->IsDescendantOf(node)) { MaybeCallNotifyWinEvent( diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h index 7aec2e1c5e9..7857f68a76e 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h @@ -43,8 +43,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin // BrowserAccessibilityManager methods virtual void AddNodeToMap(BrowserAccessibility* node); virtual void RemoveNode(BrowserAccessibility* node) OVERRIDE; - virtual void NotifyAccessibilityEvent(int type, BrowserAccessibility* node) - OVERRIDE; + virtual void NotifyAccessibilityEvent( + WebKit::WebAXEvent event_type, BrowserAccessibility* node) OVERRIDE; // Track this object and post a VISIBLE_DATA_CHANGED notification when // its container scrolls. diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc index befd717bc0c..3dee7d62b33 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc @@ -10,6 +10,7 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/common/content_switches.h" #include "ui/gfx/sys_color_change_listener.h" @@ -131,16 +132,16 @@ void BrowserAccessibilityStateImpl::SetAccessibilityMode( // Iterate over all RenderWidgetHosts, even swapped out ones in case // they become active again. - RenderWidgetHost::List widgets = - RenderWidgetHostImpl::GetAllRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHostImpl::GetAllRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { // Ignore processes that don't have a connection, such as crashed tabs. - if (!widgets[i]->GetProcess()->HasConnection()) + if (!widget->GetProcess()->HasConnection()) continue; - if (!widgets[i]->IsRenderView()) + if (!widget->IsRenderView()) continue; - RenderWidgetHostImpl::From(widgets[i])->SetAccessibilityMode(mode); + RenderWidgetHostImpl::From(widget)->SetAccessibilityMode(mode); } } diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.cc b/chromium/content/browser/accessibility/browser_accessibility_win.cc index 792adc4440d..df3263e845d 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_win.cc @@ -15,6 +15,7 @@ #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "content/browser/accessibility/browser_accessibility_manager_win.h" +#include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/common/accessibility_messages.h" #include "content/public/common/content_client.h" #include "ui/base/accessibility/accessible_text_utils.h" @@ -457,7 +458,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) { if (!target) return E_INVALIDARG; - string16 name_str = target->name_; + std::string name_str = target->name(); // If the name is empty, see if it's labeled by another element. if (name_str.empty()) { @@ -474,7 +475,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) { if (name_str.empty()) return S_FALSE; - *name = SysAllocString(name_str.c_str()); + *name = SysAllocString(UTF8ToUTF16(name_str).c_str()); DCHECK(*name); return S_OK; @@ -557,8 +558,33 @@ STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id, if (!target) return E_INVALIDARG; - *value = SysAllocString(target->value_.c_str()); + if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR || + target->ia_role() == ROLE_SYSTEM_SCROLLBAR || + target->ia_role() == ROLE_SYSTEM_SLIDER) { + string16 value_text = target->GetValueText(); + *value = SysAllocString(value_text.c_str()); + DCHECK(*value); + return S_OK; + } + + // Expose color well value. + if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) { + int r = target->GetIntAttribute( + AccessibilityNodeData::ATTR_COLOR_VALUE_RED); + int g = target->GetIntAttribute( + AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN); + int b = target->GetIntAttribute( + AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE); + string16 value_text; + value_text = base::IntToString16((r * 100) / 255) + L"% red " + + base::IntToString16((g * 100) / 255) + L"% green " + + base::IntToString16((b * 100) / 255) + L"% blue"; + *value = SysAllocString(value_text.c_str()); + DCHECK(*value); + return S_OK; + } + *value = SysAllocString(UTF8ToUTF16(target->value()).c_str()); DCHECK(*value); return S_OK; } @@ -573,12 +599,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { if (!instance_active_) return E_FAIL; - if (role_ != AccessibilityNodeData::ROLE_LISTBOX) + if (role_ != WebKit::WebAXRoleListBox) return E_NOTIMPL; unsigned long selected_count = 0; for (size_t i = 0; i < children_.size(); ++i) { - if (children_[i]->HasState(AccessibilityNodeData::STATE_SELECTED)) + if (children_[i]->HasState(WebKit::WebAXStateSelected)) ++selected_count; } @@ -589,7 +615,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { if (selected_count == 1) { for (size_t i = 0; i < children_.size(); ++i) { - if (children_[i]->HasState(AccessibilityNodeData::STATE_SELECTED)) { + if (children_[i]->HasState(WebKit::WebAXStateSelected)) { selected->vt = VT_DISPATCH; selected->pdispVal = children_[i]->ToBrowserAccessibilityWin()->NewReference(); @@ -604,7 +630,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { enum_variant->AddRef(); unsigned long index = 0; for (size_t i = 0; i < children_.size(); ++i) { - if (children_[i]->HasState(AccessibilityNodeData::STATE_SELECTED)) { + if (children_[i]->HasState(WebKit::WebAXStateSelected)) { enum_variant->ItemAt(index)->vt = VT_DISPATCH; enum_variant->ItemAt(index)->pdispVal = children_[i]->ToBrowserAccessibilityWin()->NewReference(); @@ -845,9 +871,9 @@ STDMETHODIMP BrowserAccessibilityWin::get_groupPosition( if (!group_level || !similar_items_in_group || !position_in_group) return E_INVALIDARG; - if (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION && + if (role_ == WebKit::WebAXRoleListBoxOption && parent_ && - parent_->role() == AccessibilityNodeData::ROLE_LISTBOX) { + parent_->role() == WebKit::WebAXRoleListBox) { *group_level = 0; *similar_items_in_group = parent_->child_count(); *position_in_group = index_in_parent_ + 1; @@ -1014,9 +1040,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt( if (row < 0 || row >= rows || column < 0 || column >= columns) return E_INVALIDARG; - DCHECK_EQ(columns * rows, static_cast<int>(cell_ids_.size())); + const std::vector<int32>& cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); + DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size())); - int cell_id = cell_ids_[row * columns + column]; + int cell_id = cell_ids[row * columns + column]; BrowserAccessibilityWin* cell = GetFromRendererID(cell_id); if (cell) { *accessible = static_cast<IAccessible*>(cell->NewReference()); @@ -1061,10 +1089,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row, if (row < 0 || row >= rows || column < 0 || column >= columns) return E_INVALIDARG; - DCHECK_EQ(columns * rows, static_cast<int>(cell_ids_.size())); - int cell_id = cell_ids_[row * columns + column]; - for (size_t i = 0; i < unique_cell_ids_.size(); ++i) { - if (unique_cell_ids_[i] == cell_id) { + const std::vector<int32>& cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); + const std::vector<int32>& unique_cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); + DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size())); + int cell_id = cell_ids[row * columns + column]; + for (size_t i = 0; i < unique_cell_ids.size(); ++i) { + if (unique_cell_ids[i] == cell_id) { *cell_index = (long)i; return S_OK; } @@ -1094,13 +1126,17 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column, if (column < 0 || column >= columns) return E_INVALIDARG; + const std::vector<int32>& cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); for (int i = 0; i < rows; ++i) { - int cell_id = cell_ids_[i * columns + column]; + int cell_id = cell_ids[i * columns + column]; BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>( manager_->GetFromRendererID(cell_id)); - if (cell && cell->role_ == AccessibilityNodeData::ROLE_COLUMN_HEADER) { - if (cell->name_.size() > 0) { - *description = SysAllocString(cell->name_.c_str()); + if (cell && cell->role_ == WebKit::WebAXRoleColumnHeader) { + string16 cell_name = cell->GetString16Attribute( + AccessibilityNodeData::ATTR_NAME); + if (cell_name.size() > 0) { + *description = SysAllocString(cell_name.c_str()); return S_OK; } @@ -1135,7 +1171,9 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt( if (row < 0 || row >= rows || column < 0 || column >= columns) return E_INVALIDARG; - int cell_id = cell_ids_[row * columns + column]; + const std::vector<int32>& cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); + int cell_id = cell_ids[row * columns + column]; BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>( manager_->GetFromRendererID(cell_id)); int colspan; @@ -1165,13 +1203,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index, if (!column_index) return E_INVALIDARG; - int cell_id_count = static_cast<int>(unique_cell_ids_.size()); + const std::vector<int32>& unique_cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); + int cell_id_count = static_cast<int>(unique_cell_ids.size()); if (cell_index < 0) return E_INVALIDARG; if (cell_index >= cell_id_count) return S_FALSE; - int cell_id = unique_cell_ids_[cell_index]; + int cell_id = unique_cell_ids[cell_index]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); int col_index; @@ -1273,13 +1313,17 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row, if (row < 0 || row >= rows) return E_INVALIDARG; + const std::vector<int32>& cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); for (int i = 0; i < columns; ++i) { - int cell_id = cell_ids_[row * columns + i]; + int cell_id = cell_ids[row * columns + i]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); - if (cell && cell->role_ == AccessibilityNodeData::ROLE_ROW_HEADER) { - if (cell->name_.size() > 0) { - *description = SysAllocString(cell->name_.c_str()); + if (cell && cell->role_ == WebKit::WebAXRoleRowHeader) { + string16 cell_name = cell->GetString16Attribute( + AccessibilityNodeData::ATTR_NAME); + if (cell_name.size() > 0) { + *description = SysAllocString(cell_name.c_str()); return S_OK; } @@ -1313,7 +1357,9 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row, if (row < 0 || row >= rows || column < 0 || column >= columns) return E_INVALIDARG; - int cell_id = cell_ids_[row * columns + column]; + const std::vector<int32>& cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); + int cell_id = cell_ids[row * columns + column]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); int rowspan; @@ -1343,13 +1389,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index, if (!row_index) return E_INVALIDARG; - int cell_id_count = static_cast<int>(unique_cell_ids_.size()); + const std::vector<int32>& unique_cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); + int cell_id_count = static_cast<int>(unique_cell_ids.size()); if (cell_index < 0) return E_INVALIDARG; if (cell_index >= cell_id_count) return S_FALSE; - int cell_id = unique_cell_ids_[cell_index]; + int cell_id = unique_cell_ids[cell_index]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); int cell_row_index; @@ -1470,13 +1518,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex( if (!row || !column || !row_extents || !column_extents || !is_selected) return E_INVALIDARG; - int cell_id_count = static_cast<int>(unique_cell_ids_.size()); + const std::vector<int32>& unique_cell_ids = GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); + int cell_id_count = static_cast<int>(unique_cell_ids.size()); if (index < 0) return E_INVALIDARG; if (index >= cell_id_count) return S_FALSE; - int cell_id = unique_cell_ids_[index]; + int cell_id = unique_cell_ids[index]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); int rowspan; @@ -1592,7 +1642,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( } BrowserAccessibility* table = parent(); - while (table && table->role() != AccessibilityNodeData::ROLE_TABLE) + while (table && table->role() != WebKit::WebAXRoleTable) table = table->parent(); if (!table) { NOTREACHED(); @@ -1610,11 +1660,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( if (columns <= 0 || rows <= 0 || column < 0 || column >= columns) return S_FALSE; + const std::vector<int32>& cell_ids = table->GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); + for (int i = 0; i < rows; ++i) { - int cell_id = table->cell_ids()[i * columns + column]; + int cell_id = cell_ids[i * columns + column]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); - if (cell && cell->role_ == AccessibilityNodeData::ROLE_COLUMN_HEADER) + if (cell && cell->role_ == WebKit::WebAXRoleColumnHeader) (*n_column_header_cells)++; } @@ -1622,10 +1675,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( (*n_column_header_cells) * sizeof(cell_accessibles[0]))); int index = 0; for (int i = 0; i < rows; ++i) { - int cell_id = table->cell_ids()[i * columns + column]; + int cell_id = cell_ids[i * columns + column]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); - if (cell && cell->role_ == AccessibilityNodeData::ROLE_COLUMN_HEADER) { + if (cell && cell->role_ == WebKit::WebAXRoleColumnHeader) { (*cell_accessibles)[index] = static_cast<IAccessible*>(cell->NewReference()); ++index; @@ -1688,7 +1741,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( } BrowserAccessibility* table = parent(); - while (table && table->role() != AccessibilityNodeData::ROLE_TABLE) + while (table && table->role() != WebKit::WebAXRoleTable) table = table->parent(); if (!table) { NOTREACHED(); @@ -1706,11 +1759,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( if (columns <= 0 || rows <= 0 || row < 0 || row >= rows) return S_FALSE; + const std::vector<int32>& cell_ids = table->GetIntListAttribute( + AccessibilityNodeData::ATTR_CELL_IDS); + for (int i = 0; i < columns; ++i) { - int cell_id = table->cell_ids()[row * columns + i]; + int cell_id = cell_ids[row * columns + i]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); - if (cell && cell->role_ == AccessibilityNodeData::ROLE_ROW_HEADER) + if (cell && cell->role_ == WebKit::WebAXRoleRowHeader) (*n_row_header_cells)++; } @@ -1718,10 +1774,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( (*n_row_header_cells) * sizeof(cell_accessibles[0]))); int index = 0; for (int i = 0; i < columns; ++i) { - int cell_id = table->cell_ids()[row * columns + i]; + int cell_id = cell_ids[row * columns + i]; BrowserAccessibilityWin* cell = manager_->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin(); - if (cell && cell->role_ == AccessibilityNodeData::ROLE_ROW_HEADER) { + if (cell && cell->role_ == WebKit::WebAXRoleRowHeader) { (*cell_accessibles)[index] = static_cast<IAccessible*>(cell->NewReference()); ++index; @@ -1810,7 +1866,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) { GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column); BrowserAccessibility* find_table = parent(); - while (find_table && find_table->role() != AccessibilityNodeData::ROLE_TABLE) + while (find_table && find_table->role() != WebKit::WebAXRoleTable) find_table = find_table->parent(); if (!find_table) { NOTREACHED(); @@ -1846,8 +1902,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { return E_INVALIDARG; *offset = 0; - if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD || - role_ == AccessibilityNodeData::ROLE_TEXTAREA) { + if (role_ == WebKit::WebAXRoleTextField || + role_ == WebKit::WebAXRoleTextArea) { int sel_start = 0; if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start)) @@ -1865,8 +1921,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) { return E_INVALIDARG; *n_selections = 0; - if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD || - role_ == AccessibilityNodeData::ROLE_TEXTAREA) { + if (role_ == WebKit::WebAXRoleTextField || + role_ == WebKit::WebAXRoleTextArea) { int sel_start = 0; int sel_end = 0; if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START, @@ -1890,8 +1946,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, *start_offset = 0; *end_offset = 0; - if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD || - role_ == AccessibilityNodeData::ROLE_TEXTAREA) { + if (role_ == WebKit::WebAXRoleTextField || + role_ == WebKit::WebAXRoleTextArea) { int sel_start = 0; int sel_end = 0; if (GetIntAttribute( @@ -2334,13 +2390,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo( } string16 tag; - if (GetStringAttribute(AccessibilityNodeData::ATTR_HTML_TAG, &tag)) + if (GetString16Attribute(AccessibilityNodeData::ATTR_HTML_TAG, &tag)) *node_name = SysAllocString(tag.c_str()); else *node_name = NULL; *name_space_id = 0; - *node_value = SysAllocString(value_.c_str()); + *node_value = SysAllocString(UTF8ToUTF16(value_).c_str()); *num_children = children_.size(); *unique_id = unique_id_win_; @@ -2373,9 +2429,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_attributes( *num_attribs = html_attributes_.size(); for (unsigned short i = 0; i < *num_attribs; ++i) { - attrib_names[i] = SysAllocString(html_attributes_[i].first.c_str()); + attrib_names[i] = SysAllocString( + UTF8ToUTF16(html_attributes_[i].first).c_str()); name_space_id[i] = 0; - attrib_values[i] = SysAllocString(html_attributes_[i].second.c_str()); + attrib_values[i] = SysAllocString( + UTF8ToUTF16(html_attributes_[i].second).c_str()); } return S_OK; } @@ -2394,10 +2452,11 @@ STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames( for (unsigned short i = 0; i < num_attribs; ++i) { name_space_id[i] = 0; bool found = false; - string16 name = (LPCWSTR)attrib_names[i]; + std::string name = UTF16ToUTF8((LPCWSTR)attrib_names[i]); for (unsigned int j = 0; j < html_attributes_.size(); ++j) { if (html_attributes_[j].first == name) { - attrib_values[i] = SysAllocString(html_attributes_[j].second.c_str()); + attrib_values[i] = SysAllocString( + UTF8ToUTF16(html_attributes_[j].second).c_str()); found = true; break; } @@ -2425,7 +2484,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_computedStyle( string16 display; if (max_style_properties == 0 || - !GetStringAttribute(AccessibilityNodeData::ATTR_DISPLAY, &display)) { + !GetString16Attribute(AccessibilityNodeData::ATTR_DISPLAY, &display)) { *num_style_properties = 0; return S_OK; } @@ -2454,8 +2513,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties( string16 name = (LPCWSTR)style_properties[i]; StringToLowerASCII(&name); if (name == L"display") { - string16 display; - GetStringAttribute(AccessibilityNodeData::ATTR_DISPLAY, &display); + string16 display = GetString16Attribute( + AccessibilityNodeData::ATTR_DISPLAY); style_values[i] = SysAllocString(display.c_str()); } else { style_values[i] = NULL; @@ -2579,12 +2638,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) { if (!dom_text) return E_INVALIDARG; - if (name_.empty()) - return S_FALSE; - - *dom_text = SysAllocString(name_.c_str()); - DCHECK(*dom_text); - return S_OK; + return GetStringAttributeAsBstr( + AccessibilityNodeData::ATTR_NAME, dom_text); } // @@ -2597,6 +2652,12 @@ STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService, if (!instance_active_) return E_FAIL; + // The system uses IAccessible APIs for many purposes, but only + // assistive technology like screen readers uses IAccessible2. + // Enable full accessibility support when IAccessible2 APIs are queried. + if (riid == IID_IAccessible2) + BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); + if (guidService == GUID_IAccessibleContentDocument) { // Special Mozilla extension: return the accessible for the root document. // Screen readers use this to distinguish between a document loaded event @@ -2738,9 +2799,9 @@ void BrowserAccessibilityWin::PreInitialize() { IntAttributeToIA2(AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, "level"); // Expose the set size and position in set for listbox options. - if (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION && + if (role_ == WebKit::WebAXRoleListBoxOption && parent_ && - parent_->role() == AccessibilityNodeData::ROLE_LISTBOX) { + parent_->role() == WebKit::WebAXRoleListBox) { ia2_attributes_.push_back( L"setsize:" + base::IntToString16(parent_->child_count())); ia2_attributes_.push_back( @@ -2773,33 +2834,17 @@ void BrowserAccessibilityWin::PreInitialize() { if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR || ia_role_ == ROLE_SYSTEM_SCROLLBAR || ia_role_ == ROLE_SYSTEM_SLIDER) { - float fval; - if (value_.empty() && - GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &fval)) { - // TODO(dmazzoni): Use ICU to localize this? - value_ = UTF8ToUTF16(base::DoubleToString(fval)); - } - ia2_attributes_.push_back(L"valuetext:" + value_); - } - - // Expose color well value. - if (ia2_role_ == IA2_ROLE_COLOR_CHOOSER) { - int r, g, b; - GetIntAttribute(AccessibilityNodeData::ATTR_COLOR_VALUE_RED, &r); - GetIntAttribute(AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN, &g); - GetIntAttribute(AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE, &b); - value_ = base::IntToString16((r * 100) / 255) + L"% red " + - base::IntToString16((g * 100) / 255) + L"% green " + - base::IntToString16((b * 100) / 255) + L"% blue"; + ia2_attributes_.push_back(L"valuetext:" + GetValueText()); } // Expose table cell index. if (ia_role_ == ROLE_SYSTEM_CELL) { BrowserAccessibility* table = parent(); - while (table && table->role() != AccessibilityNodeData::ROLE_TABLE) + while (table && table->role() != WebKit::WebAXRoleTable) table = table->parent(); if (table) { - const std::vector<int32>& unique_cell_ids = table->unique_cell_ids(); + const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute( + AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS); for (size_t i = 0; i < unique_cell_ids.size(); ++i) { if (unique_cell_ids[i] == renderer_id_) { ia2_attributes_.push_back( @@ -2830,22 +2875,21 @@ void BrowserAccessibilityWin::PreInitialize() { // always returns the primary name in "name" and the secondary name, // if any, in "description". - string16 description, help, title_attr; - int title_elem_id = 0; - GetIntAttribute(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &title_elem_id); - GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description); - GetStringAttribute(AccessibilityNodeData::ATTR_HELP, &help); + int title_elem_id = GetIntAttribute( + AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT); + std::string help = GetStringAttribute(AccessibilityNodeData::ATTR_HELP); + std::string description = GetStringAttribute( + AccessibilityNodeData::ATTR_DESCRIPTION); // WebKit annoyingly puts the title in the description if there's no other // description, which just confuses the rest of the logic. Put it back. // Now "help" is always the value of the "title" attribute, if present. + std::string title_attr; if (GetHtmlAttribute("title", &title_attr) && description == title_attr && help.empty()) { help = description; description.clear(); - string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION].clear(); - string_attributes_[AccessibilityNodeData::ATTR_HELP] = help; } // Now implement the main logic: the descripion should become the name if @@ -2854,35 +2898,34 @@ void BrowserAccessibilityWin::PreInitialize() { if (!description.empty()) { name_ = description; description.clear(); - string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = description; } if (!help.empty() && description.empty()) { description = help; - string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = help; - string_attributes_[AccessibilityNodeData::ATTR_HELP].clear(); + help.clear(); } if (!description.empty() && name_.empty() && !title_elem_id) { name_ = description; description.clear(); - string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION].clear(); } // If it's a text field, also consider the placeholder. - string16 placeholder; - if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD && - HasState(AccessibilityNodeData::STATE_FOCUSABLE) && + std::string placeholder; + if (role_ == WebKit::WebAXRoleTextField && + HasState(WebKit::WebAXStateFocusable) && GetHtmlAttribute("placeholder", &placeholder)) { if (name_.empty() && !title_elem_id) { name_ = placeholder; } else if (description.empty()) { description = placeholder; - string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = description; } } + SetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, description); + SetStringAttribute(AccessibilityNodeData::ATTR_HELP, help); + // On Windows, the value of a document should be its url. - if (role_ == AccessibilityNodeData::ROLE_ROOT_WEB_AREA || - role_ == AccessibilityNodeData::ROLE_WEB_AREA) { + if (role_ == WebKit::WebAXRoleRootWebArea || + role_ == WebKit::WebAXRoleWebArea) { GetStringAttribute(AccessibilityNodeData::ATTR_DOC_URL, &value_); } @@ -2890,15 +2933,14 @@ void BrowserAccessibilityWin::PreInitialize() { // WebKit stores the main accessible text in the "value" - swap it so // that it's the "name". if (name_.empty() && - (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION || - role_ == AccessibilityNodeData::ROLE_STATIC_TEXT || - role_ == AccessibilityNodeData::ROLE_LIST_MARKER)) { + (role_ == WebKit::WebAXRoleListBoxOption || + role_ == WebKit::WebAXRoleStaticText || + role_ == WebKit::WebAXRoleListMarker)) { name_.swap(value_); } // If this doesn't have a value and is linked then set its value to the url // attribute. This allows screen readers to read an empty link's destination. - string16 url; if (value_.empty() && (ia_state_ & STATE_SYSTEM_LINKED)) GetStringAttribute(AccessibilityNodeData::ATTR_URL, &value_); @@ -2930,8 +2972,8 @@ void BrowserAccessibilityWin::PostInitialize() { hypertext_.clear(); for (unsigned int i = 0; i < children().size(); ++i) { BrowserAccessibility* child = children()[i]; - if (child->role() == AccessibilityNodeData::ROLE_STATIC_TEXT) { - hypertext_ += child->name(); + if (child->role() == WebKit::WebAXRoleStaticText) { + hypertext_ += UTF8ToUTF16(child->name()); } else { hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size(); hypertext_ += kEmbeddedCharacter; @@ -2941,15 +2983,15 @@ void BrowserAccessibilityWin::PostInitialize() { DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size()); // Fire an event when an alert first appears. - if (role_ == AccessibilityNodeData::ROLE_ALERT && first_time_) - manager_->NotifyAccessibilityEvent(AccessibilityNotificationAlert, this); + if (role_ == WebKit::WebAXRoleAlert && first_time_) + manager_->NotifyAccessibilityEvent(WebKit::WebAXEventAlert, this); // Fire events if text has changed. string16 text = TextForIAccessibleText(); if (previous_text_ != text) { if (!previous_text_.empty() && !text.empty()) { manager_->NotifyAccessibilityEvent( - AccessibilityNotificationObjectShow, this); + WebKit::WebAXEventShow, this); } // TODO(dmazzoni): Look into HIDE events, too. @@ -2967,7 +3009,7 @@ void BrowserAccessibilityWin::PostInitialize() { // focus for managed descendants is platform-specific. // Fire a focus event if the focused descendant in a multi-select // list box changes. - if (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION && + if (role_ == WebKit::WebAXRoleListBoxOption && (ia_state_ & STATE_SYSTEM_FOCUSABLE) && (ia_state_ & STATE_SYSTEM_SELECTABLE) && (ia_state_ & STATE_SYSTEM_FOCUSED) && @@ -3035,7 +3077,7 @@ HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr( BSTR* value_bstr) { string16 str; - if (!GetStringAttribute(attribute, &str)) + if (!GetString16Attribute(attribute, &str)) return S_FALSE; if (str.empty()) @@ -3051,7 +3093,7 @@ void BrowserAccessibilityWin::StringAttributeToIA2( AccessibilityNodeData::StringAttribute attribute, const char* ia2_attr) { string16 value; - if (GetStringAttribute(attribute, &value)) + if (GetString16Attribute(attribute, &value)) ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + value); } @@ -3069,16 +3111,27 @@ void BrowserAccessibilityWin::IntAttributeToIA2( AccessibilityNodeData::IntAttribute attribute, const char* ia2_attr) { int value; - if (GetIntAttribute(attribute, &value)) + if (GetIntAttribute(attribute, &value)) { ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + base::IntToString16(value)); + } +} + +string16 BrowserAccessibilityWin::GetValueText() { + float fval; + string16 value = UTF8ToUTF16(value_); + if (value.empty() && + GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &fval)) { + value = UTF8ToUTF16(base::DoubleToString(fval)); + } + return value; } -const string16& BrowserAccessibilityWin::TextForIAccessibleText() { +string16 BrowserAccessibilityWin::TextForIAccessibleText() { if (IsEditableText()) - return value_; - return (role_ == AccessibilityNodeData::ROLE_STATIC_TEXT) ? - name_ : hypertext_; + return UTF8ToUTF16(value_); + return (role_ == WebKit::WebAXRoleStaticText) ? + UTF8ToUTF16(name_) : hypertext_; } void BrowserAccessibilityWin::HandleSpecialTextOffset(const string16& text, @@ -3111,8 +3164,10 @@ LONG BrowserAccessibilityWin::FindBoundary( ui::TextBoundaryDirection direction) { HandleSpecialTextOffset(text, &start_offset); ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); + const std::vector<int32>& line_breaks = GetIntListAttribute( + AccessibilityNodeData::ATTR_LINE_BREAKS); return ui::FindAccessibleTextBoundary( - text, line_breaks_, boundary, start_offset, direction); + text, line_breaks, boundary, start_offset, direction); } BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromRendererID( @@ -3125,99 +3180,95 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia2_state_ = IA2_STATE_OPAQUE; ia2_attributes_.clear(); - if (HasState(AccessibilityNodeData::STATE_BUSY)) + if (HasState(WebKit::WebAXStateBusy)) ia_state_ |= STATE_SYSTEM_BUSY; - if (HasState(AccessibilityNodeData::STATE_CHECKED)) + if (HasState(WebKit::WebAXStateChecked)) ia_state_ |= STATE_SYSTEM_CHECKED; - if (HasState(AccessibilityNodeData::STATE_COLLAPSED)) + if (HasState(WebKit::WebAXStateCollapsed)) ia_state_ |= STATE_SYSTEM_COLLAPSED; - if (HasState(AccessibilityNodeData::STATE_EXPANDED)) + if (HasState(WebKit::WebAXStateExpanded)) ia_state_ |= STATE_SYSTEM_EXPANDED; - if (HasState(AccessibilityNodeData::STATE_FOCUSABLE)) + if (HasState(WebKit::WebAXStateFocusable)) ia_state_ |= STATE_SYSTEM_FOCUSABLE; - if (HasState(AccessibilityNodeData::STATE_HASPOPUP)) + if (HasState(WebKit::WebAXStateHaspopup)) ia_state_ |= STATE_SYSTEM_HASPOPUP; - if (HasState(AccessibilityNodeData::STATE_HOTTRACKED)) + if (HasState(WebKit::WebAXStateHovered)) ia_state_ |= STATE_SYSTEM_HOTTRACKED; - if (HasState(AccessibilityNodeData::STATE_INDETERMINATE)) + if (HasState(WebKit::WebAXStateIndeterminate)) ia_state_ |= STATE_SYSTEM_INDETERMINATE; - if (HasState(AccessibilityNodeData::STATE_INVISIBLE)) + if (HasState(WebKit::WebAXStateInvisible)) ia_state_ |= STATE_SYSTEM_INVISIBLE; - if (HasState(AccessibilityNodeData::STATE_LINKED)) + if (HasState(WebKit::WebAXStateLinked)) ia_state_ |= STATE_SYSTEM_LINKED; - if (HasState(AccessibilityNodeData::STATE_MULTISELECTABLE)) { + if (HasState(WebKit::WebAXStateMultiselectable)) { ia_state_ |= STATE_SYSTEM_EXTSELECTABLE; ia_state_ |= STATE_SYSTEM_MULTISELECTABLE; } // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect. - if (HasState(AccessibilityNodeData::STATE_OFFSCREEN)) + if (HasState(WebKit::WebAXStateOffscreen)) ia_state_ |= STATE_SYSTEM_OFFSCREEN; - if (HasState(AccessibilityNodeData::STATE_PRESSED)) + if (HasState(WebKit::WebAXStatePressed)) ia_state_ |= STATE_SYSTEM_PRESSED; - if (HasState(AccessibilityNodeData::STATE_PROTECTED)) + if (HasState(WebKit::WebAXStateProtected)) ia_state_ |= STATE_SYSTEM_PROTECTED; - if (HasState(AccessibilityNodeData::STATE_REQUIRED)) + if (HasState(WebKit::WebAXStateRequired)) ia2_state_ |= IA2_STATE_REQUIRED; - if (HasState(AccessibilityNodeData::STATE_SELECTABLE)) + if (HasState(WebKit::WebAXStateSelectable)) ia_state_ |= STATE_SYSTEM_SELECTABLE; - if (HasState(AccessibilityNodeData::STATE_SELECTED)) + if (HasState(WebKit::WebAXStateSelected)) ia_state_ |= STATE_SYSTEM_SELECTED; - if (HasState(AccessibilityNodeData::STATE_TRAVERSED)) + if (HasState(WebKit::WebAXStateVisited)) ia_state_ |= STATE_SYSTEM_TRAVERSED; - if (HasState(AccessibilityNodeData::STATE_UNAVAILABLE)) + if (!HasState(WebKit::WebAXStateEnabled)) ia_state_ |= STATE_SYSTEM_UNAVAILABLE; - if (HasState(AccessibilityNodeData::STATE_VERTICAL)) { + if (HasState(WebKit::WebAXStateVertical)) { ia2_state_ |= IA2_STATE_VERTICAL; } else { ia2_state_ |= IA2_STATE_HORIZONTAL; } - if (HasState(AccessibilityNodeData::STATE_VISITED)) + if (HasState(WebKit::WebAXStateVisited)) ia_state_ |= STATE_SYSTEM_TRAVERSED; // WebKit marks everything as readonly unless it's editable text, so if it's // not readonly, mark it as editable now. The final computation of the // READONLY state for MSAA is below, after the switch. - if (!HasState(AccessibilityNodeData::STATE_READONLY)) + if (!HasState(WebKit::WebAXStateReadonly)) ia2_state_ |= IA2_STATE_EDITABLE; string16 invalid; if (GetHtmlAttribute("aria-invalid", &invalid)) ia2_state_ |= IA2_STATE_INVALID_ENTRY; - bool mixed = false; - GetBoolAttribute(AccessibilityNodeData::ATTR_BUTTON_MIXED, &mixed); - if (mixed) + if (GetBoolAttribute(AccessibilityNodeData::ATTR_BUTTON_MIXED)) ia_state_ |= STATE_SYSTEM_MIXED; - bool editable = false; - GetBoolAttribute(AccessibilityNodeData::ATTR_CAN_SET_VALUE, &editable); - if (editable) + if (GetBoolAttribute(AccessibilityNodeData::ATTR_CAN_SET_VALUE)) ia2_state_ |= IA2_STATE_EDITABLE; - string16 html_tag; - GetStringAttribute(AccessibilityNodeData::ATTR_HTML_TAG, &html_tag); + string16 html_tag = GetString16Attribute( + AccessibilityNodeData::ATTR_HTML_TAG); ia_role_ = 0; ia2_role_ = 0; switch (role_) { - case AccessibilityNodeData::ROLE_ALERT: + case WebKit::WebAXRoleAlert: ia_role_ = ROLE_SYSTEM_ALERT; break; - case AccessibilityNodeData::ROLE_ALERT_DIALOG: + case WebKit::WebAXRoleAlertDialog: ia_role_ = ROLE_SYSTEM_DIALOG; break; - case AccessibilityNodeData::ROLE_APPLICATION: + case WebKit::WebAXRoleApplication: ia_role_ = ROLE_SYSTEM_APPLICATION; break; - case AccessibilityNodeData::ROLE_ARTICLE: + case WebKit::WebAXRoleArticle: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_BUSY_INDICATOR: + case WebKit::WebAXRoleBusyIndicator: ia_role_ = ROLE_SYSTEM_ANIMATION; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_BUTTON: + case WebKit::WebAXRoleButton: ia_role_ = ROLE_SYSTEM_PUSHBUTTON; bool is_aria_pressed_defined; bool is_mixed; @@ -3228,87 +3279,88 @@ void BrowserAccessibilityWin::InitRoleAndState() { if (is_mixed) ia_state_ |= STATE_SYSTEM_MIXED; break; - case AccessibilityNodeData::ROLE_CANVAS: - ia_role_ = ROLE_SYSTEM_GRAPHIC; - break; - case AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT: - role_name_ = L"canvas"; - ia2_role_ = IA2_ROLE_CANVAS; + case WebKit::WebAXRoleCanvas: + if (GetBoolAttribute(AccessibilityNodeData::ATTR_CANVAS_HAS_FALLBACK)) { + role_name_ = L"canvas"; + ia2_role_ = IA2_ROLE_CANVAS; + } else { + ia_role_ = ROLE_SYSTEM_GRAPHIC; + } break; - case AccessibilityNodeData::ROLE_CELL: + case WebKit::WebAXRoleCell: ia_role_ = ROLE_SYSTEM_CELL; break; - case AccessibilityNodeData::ROLE_CHECKBOX: + case WebKit::WebAXRoleCheckBox: ia_role_ = ROLE_SYSTEM_CHECKBUTTON; break; - case AccessibilityNodeData::ROLE_COLOR_WELL: + case WebKit::WebAXRoleColorWell: ia_role_ = ROLE_SYSTEM_CLIENT; ia2_role_ = IA2_ROLE_COLOR_CHOOSER; break; - case AccessibilityNodeData::ROLE_COLUMN: + case WebKit::WebAXRoleColumn: ia_role_ = ROLE_SYSTEM_COLUMN; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_COLUMN_HEADER: + case WebKit::WebAXRoleColumnHeader: ia_role_ = ROLE_SYSTEM_COLUMNHEADER; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_COMBO_BOX: + case WebKit::WebAXRoleComboBox: ia_role_ = ROLE_SYSTEM_COMBOBOX; break; - case AccessibilityNodeData::ROLE_DIV: + case WebKit::WebAXRoleDiv: role_name_ = L"div"; ia2_role_ = IA2_ROLE_SECTION; break; - case AccessibilityNodeData::ROLE_DEFINITION: + case WebKit::WebAXRoleDefinition: role_name_ = html_tag; ia2_role_ = IA2_ROLE_PARAGRAPH; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_DESCRIPTION_LIST_DETAIL: + case WebKit::WebAXRoleDescriptionListDetail: role_name_ = html_tag; ia2_role_ = IA2_ROLE_PARAGRAPH; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_DESCRIPTION_LIST_TERM: + case WebKit::WebAXRoleDescriptionListTerm: ia_role_ = ROLE_SYSTEM_LISTITEM; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_DIALOG: + case WebKit::WebAXRoleDialog: ia_role_ = ROLE_SYSTEM_DIALOG; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE: + case WebKit::WebAXRoleDisclosureTriangle: ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_DOCUMENT: - case AccessibilityNodeData::ROLE_ROOT_WEB_AREA: - case AccessibilityNodeData::ROLE_WEB_AREA: + case WebKit::WebAXRoleDocument: + case WebKit::WebAXRoleRootWebArea: + case WebKit::WebAXRoleWebArea: ia_role_ = ROLE_SYSTEM_DOCUMENT; ia_state_ |= STATE_SYSTEM_READONLY; ia_state_ |= STATE_SYSTEM_FOCUSABLE; break; - case AccessibilityNodeData::ROLE_EDITABLE_TEXT: + case WebKit::WebAXRoleEditableText: ia_role_ = ROLE_SYSTEM_TEXT; ia2_state_ |= IA2_STATE_SINGLE_LINE; ia2_state_ |= IA2_STATE_EDITABLE; break; - case AccessibilityNodeData::ROLE_FORM: + case WebKit::WebAXRoleForm: role_name_ = L"form"; ia2_role_ = IA2_ROLE_FORM; break; - case AccessibilityNodeData::ROLE_FOOTER: + case WebKit::WebAXRoleFooter: ia_role_ = IA2_ROLE_FOOTER; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_GRID: + case WebKit::WebAXRoleGrid: ia_role_ = ROLE_SYSTEM_TABLE; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_GROUP: { - string16 aria_role; - GetStringAttribute(AccessibilityNodeData::ATTR_ROLE, &aria_role); + case WebKit::WebAXRoleGroup: { + string16 aria_role = GetString16Attribute( + AccessibilityNodeData::ATTR_ROLE); if (aria_role == L"group" || html_tag == L"fieldset") { ia_role_ = ROLE_SYSTEM_GROUPING; } else if (html_tag == L"li") { @@ -3323,192 +3375,190 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia_state_ |= STATE_SYSTEM_READONLY; break; } - case AccessibilityNodeData::ROLE_GROW_AREA: + case WebKit::WebAXRoleGrowArea: ia_role_ = ROLE_SYSTEM_GRIP; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_HEADING: + case WebKit::WebAXRoleHeading: role_name_ = html_tag; ia2_role_ = IA2_ROLE_HEADING; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_HORIZONTAL_RULE: + case WebKit::WebAXRoleHorizontalRule: ia_role_ = ROLE_SYSTEM_SEPARATOR; break; - case AccessibilityNodeData::ROLE_IMAGE: + case WebKit::WebAXRoleImage: ia_role_ = ROLE_SYSTEM_GRAPHIC; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_IMAGE_MAP: + case WebKit::WebAXRoleImageMap: role_name_ = html_tag; ia2_role_ = IA2_ROLE_IMAGE_MAP; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_IMAGE_MAP_LINK: + case WebKit::WebAXRoleImageMapLink: ia_role_ = ROLE_SYSTEM_LINK; ia_state_ |= STATE_SYSTEM_LINKED; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_LABEL: + case WebKit::WebAXRoleLabel: ia_role_ = ROLE_SYSTEM_TEXT; ia2_role_ = IA2_ROLE_LABEL; break; - case AccessibilityNodeData::ROLE_LANDMARK_APPLICATION: - case AccessibilityNodeData::ROLE_LANDMARK_BANNER: - case AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY: - case AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO: - case AccessibilityNodeData::ROLE_LANDMARK_MAIN: - case AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION: - case AccessibilityNodeData::ROLE_LANDMARK_SEARCH: + case WebKit::WebAXRoleBanner: + case WebKit::WebAXRoleComplementary: + case WebKit::WebAXRoleContentInfo: + case WebKit::WebAXRoleMain: + case WebKit::WebAXRoleNavigation: + case WebKit::WebAXRoleSearch: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_LINK: - case AccessibilityNodeData::ROLE_WEBCORE_LINK: + case WebKit::WebAXRoleLink: ia_role_ = ROLE_SYSTEM_LINK; ia_state_ |= STATE_SYSTEM_LINKED; break; - case AccessibilityNodeData::ROLE_LIST: + case WebKit::WebAXRoleList: ia_role_ = ROLE_SYSTEM_LIST; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_LISTBOX: + case WebKit::WebAXRoleListBox: ia_role_ = ROLE_SYSTEM_LIST; break; - case AccessibilityNodeData::ROLE_LISTBOX_OPTION: + case WebKit::WebAXRoleListBoxOption: ia_role_ = ROLE_SYSTEM_LISTITEM; if (ia_state_ & STATE_SYSTEM_SELECTABLE) { ia_state_ |= STATE_SYSTEM_FOCUSABLE; - if (HasState(AccessibilityNodeData::STATE_FOCUSED)) + if (HasState(WebKit::WebAXStateFocused)) ia_state_ |= STATE_SYSTEM_FOCUSED; } break; - case AccessibilityNodeData::ROLE_LIST_ITEM: + case WebKit::WebAXRoleListItem: ia_role_ = ROLE_SYSTEM_LISTITEM; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_LIST_MARKER: + case WebKit::WebAXRoleListMarker: ia_role_ = ROLE_SYSTEM_TEXT; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_MATH: + case WebKit::WebAXRoleMath: ia_role_ = ROLE_SYSTEM_EQUATION; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_MENU: - case AccessibilityNodeData::ROLE_MENU_BUTTON: + case WebKit::WebAXRoleMenu: + case WebKit::WebAXRoleMenuButton: ia_role_ = ROLE_SYSTEM_MENUPOPUP; break; - case AccessibilityNodeData::ROLE_MENU_BAR: + case WebKit::WebAXRoleMenuBar: ia_role_ = ROLE_SYSTEM_MENUBAR; break; - case AccessibilityNodeData::ROLE_MENU_ITEM: + case WebKit::WebAXRoleMenuItem: ia_role_ = ROLE_SYSTEM_MENUITEM; break; - case AccessibilityNodeData::ROLE_MENU_LIST_POPUP: + case WebKit::WebAXRoleMenuListPopup: ia_role_ = ROLE_SYSTEM_CLIENT; break; - case AccessibilityNodeData::ROLE_MENU_LIST_OPTION: + case WebKit::WebAXRoleMenuListOption: ia_role_ = ROLE_SYSTEM_LISTITEM; if (ia_state_ & STATE_SYSTEM_SELECTABLE) { ia_state_ |= STATE_SYSTEM_FOCUSABLE; - if (HasState(AccessibilityNodeData::STATE_FOCUSED)) + if (HasState(WebKit::WebAXStateFocused)) ia_state_ |= STATE_SYSTEM_FOCUSED; } break; - case AccessibilityNodeData::ROLE_NOTE: + case WebKit::WebAXRoleNote: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_NOTE; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_OUTLINE: + case WebKit::WebAXRoleOutline: ia_role_ = ROLE_SYSTEM_OUTLINE; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_PARAGRAPH: + case WebKit::WebAXRoleParagraph: role_name_ = L"P"; ia2_role_ = IA2_ROLE_PARAGRAPH; break; - case AccessibilityNodeData::ROLE_POPUP_BUTTON: + case WebKit::WebAXRolePopUpButton: if (html_tag == L"select") { ia_role_ = ROLE_SYSTEM_COMBOBOX; } else { ia_role_ = ROLE_SYSTEM_BUTTONMENU; } break; - case AccessibilityNodeData::ROLE_PROGRESS_INDICATOR: + case WebKit::WebAXRoleProgressIndicator: ia_role_ = ROLE_SYSTEM_PROGRESSBAR; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_RADIO_BUTTON: + case WebKit::WebAXRoleRadioButton: ia_role_ = ROLE_SYSTEM_RADIOBUTTON; break; - case AccessibilityNodeData::ROLE_RADIO_GROUP: + case WebKit::WebAXRoleRadioGroup: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; break; - case AccessibilityNodeData::ROLE_REGION: + case WebKit::WebAXRoleRegion: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_ROW: + case WebKit::WebAXRoleRow: ia_role_ = ROLE_SYSTEM_ROW; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_ROW_HEADER: + case WebKit::WebAXRoleRowHeader: ia_role_ = ROLE_SYSTEM_ROWHEADER; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_RULER: + case WebKit::WebAXRoleRuler: ia_role_ = ROLE_SYSTEM_CLIENT; ia2_role_ = IA2_ROLE_RULER; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_SCROLLAREA: + case WebKit::WebAXRoleScrollArea: ia_role_ = ROLE_SYSTEM_CLIENT; ia2_role_ = IA2_ROLE_SCROLL_PANE; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_SCROLLBAR: + case WebKit::WebAXRoleScrollBar: ia_role_ = ROLE_SYSTEM_SCROLLBAR; break; - case AccessibilityNodeData::ROLE_SLIDER: + case WebKit::WebAXRoleSlider: ia_role_ = ROLE_SYSTEM_SLIDER; break; - case AccessibilityNodeData::ROLE_SPIN_BUTTON: + case WebKit::WebAXRoleSpinButton: ia_role_ = ROLE_SYSTEM_SPINBUTTON; break; - case AccessibilityNodeData::ROLE_SPIN_BUTTON_PART: + case WebKit::WebAXRoleSpinButtonPart: ia_role_ = ROLE_SYSTEM_PUSHBUTTON; break; - case AccessibilityNodeData::ROLE_SPLIT_GROUP: + case WebKit::WebAXRoleSplitGroup: ia_role_ = ROLE_SYSTEM_CLIENT; ia2_role_ = IA2_ROLE_SPLIT_PANE; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_ANNOTATION: - case AccessibilityNodeData::ROLE_STATIC_TEXT: + case WebKit::WebAXRoleAnnotation: + case WebKit::WebAXRoleStaticText: ia_role_ = ROLE_SYSTEM_TEXT; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_STATUS: + case WebKit::WebAXRoleStatus: ia_role_ = ROLE_SYSTEM_STATUSBAR; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_SPLITTER: + case WebKit::WebAXRoleSplitter: ia_role_ = ROLE_SYSTEM_SEPARATOR; break; - case AccessibilityNodeData::ROLE_SVG_ROOT: + case WebKit::WebAXRoleSVGRoot: ia_role_ = ROLE_SYSTEM_GRAPHIC; break; - case AccessibilityNodeData::ROLE_TAB: + case WebKit::WebAXRoleTab: ia_role_ = ROLE_SYSTEM_PAGETAB; break; - case AccessibilityNodeData::ROLE_TABLE: { - string16 aria_role; - GetStringAttribute(AccessibilityNodeData::ATTR_ROLE, &aria_role); + case WebKit::WebAXRoleTable: { + string16 aria_role = GetString16Attribute( + AccessibilityNodeData::ATTR_ROLE); if (aria_role == L"treegrid") { ia_role_ = ROLE_SYSTEM_OUTLINE; } else { @@ -3517,81 +3567,77 @@ void BrowserAccessibilityWin::InitRoleAndState() { } break; } - case AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER: + case WebKit::WebAXRoleTableHeaderContainer: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED: - NOTREACHED(); - ia_role_ = ROLE_SYSTEM_PAGETABLIST; - break; - case AccessibilityNodeData::ROLE_TAB_LIST: + case WebKit::WebAXRoleTabList: ia_role_ = ROLE_SYSTEM_PAGETABLIST; break; - case AccessibilityNodeData::ROLE_TAB_PANEL: + case WebKit::WebAXRoleTabPanel: ia_role_ = ROLE_SYSTEM_PROPERTYPAGE; break; - case AccessibilityNodeData::ROLE_TOGGLE_BUTTON: + case WebKit::WebAXRoleToggleButton: ia_role_ = ROLE_SYSTEM_PUSHBUTTON; ia2_role_ = IA2_ROLE_TOGGLE_BUTTON; break; - case AccessibilityNodeData::ROLE_TEXTAREA: + case WebKit::WebAXRoleTextArea: ia_role_ = ROLE_SYSTEM_TEXT; ia2_state_ |= IA2_STATE_MULTI_LINE; ia2_state_ |= IA2_STATE_EDITABLE; ia2_state_ |= IA2_STATE_SELECTABLE_TEXT; break; - case AccessibilityNodeData::ROLE_TEXT_FIELD: + case WebKit::WebAXRoleTextField: ia_role_ = ROLE_SYSTEM_TEXT; ia2_state_ |= IA2_STATE_SINGLE_LINE; ia2_state_ |= IA2_STATE_EDITABLE; ia2_state_ |= IA2_STATE_SELECTABLE_TEXT; break; - case AccessibilityNodeData::ROLE_TIMER: + case WebKit::WebAXRoleTimer: ia_role_ = ROLE_SYSTEM_CLOCK; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_TOOLBAR: + case WebKit::WebAXRoleToolbar: ia_role_ = ROLE_SYSTEM_TOOLBAR; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_TOOLTIP: + case WebKit::WebAXRoleUserInterfaceTooltip: ia_role_ = ROLE_SYSTEM_TOOLTIP; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_TREE: + case WebKit::WebAXRoleTree: ia_role_ = ROLE_SYSTEM_OUTLINE; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_TREE_GRID: + case WebKit::WebAXRoleTreeGrid: ia_role_ = ROLE_SYSTEM_OUTLINE; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_TREE_ITEM: + case WebKit::WebAXRoleTreeItem: ia_role_ = ROLE_SYSTEM_OUTLINEITEM; ia_state_ |= STATE_SYSTEM_READONLY; break; - case AccessibilityNodeData::ROLE_WINDOW: + case WebKit::WebAXRoleWindow: ia_role_ = ROLE_SYSTEM_WINDOW; break; // TODO(dmazzoni): figure out the proper MSAA role for all of these. - case AccessibilityNodeData::ROLE_BROWSER: - case AccessibilityNodeData::ROLE_DIRECTORY: - case AccessibilityNodeData::ROLE_DRAWER: - case AccessibilityNodeData::ROLE_HELP_TAG: - case AccessibilityNodeData::ROLE_IGNORED: - case AccessibilityNodeData::ROLE_INCREMENTOR: - case AccessibilityNodeData::ROLE_LOG: - case AccessibilityNodeData::ROLE_MARQUEE: - case AccessibilityNodeData::ROLE_MATTE: - case AccessibilityNodeData::ROLE_PRESENTATIONAL: - case AccessibilityNodeData::ROLE_RULER_MARKER: - case AccessibilityNodeData::ROLE_SHEET: - case AccessibilityNodeData::ROLE_SLIDER_THUMB: - case AccessibilityNodeData::ROLE_SYSTEM_WIDE: - case AccessibilityNodeData::ROLE_VALUE_INDICATOR: + case WebKit::WebAXRoleBrowser: + case WebKit::WebAXRoleDirectory: + case WebKit::WebAXRoleDrawer: + case WebKit::WebAXRoleHelpTag: + case WebKit::WebAXRoleIgnored: + case WebKit::WebAXRoleIncrementor: + case WebKit::WebAXRoleLog: + case WebKit::WebAXRoleMarquee: + case WebKit::WebAXRoleMatte: + case WebKit::WebAXRolePresentational: + case WebKit::WebAXRoleRulerMarker: + case WebKit::WebAXRoleSheet: + case WebKit::WebAXRoleSliderThumb: + case WebKit::WebAXRoleSystemWide: + case WebKit::WebAXRoleValueIndicator: default: ia_role_ = ROLE_SYSTEM_CLIENT; break; @@ -3603,15 +3649,13 @@ void BrowserAccessibilityWin::InitRoleAndState() { // aria-readonly attribute and for a few roles (in the switch above). // We clear the READONLY state on focusable controls and on a document. // Everything else, the majority of objects, do not have this state set. - if (HasState(AccessibilityNodeData::STATE_FOCUSABLE) && + if (HasState(WebKit::WebAXStateFocusable) && ia_role_ != ROLE_SYSTEM_DOCUMENT) { ia_state_ &= ~(STATE_SYSTEM_READONLY); } - if (!HasState(AccessibilityNodeData::STATE_READONLY)) + if (!HasState(WebKit::WebAXStateReadonly)) ia_state_ &= ~(STATE_SYSTEM_READONLY); - bool aria_readonly = false; - GetBoolAttribute(AccessibilityNodeData::ATTR_ARIA_READONLY, &aria_readonly); - if (aria_readonly) + if (GetBoolAttribute(AccessibilityNodeData::ATTR_ARIA_READONLY)) ia_state_ |= STATE_SYSTEM_READONLY; // The role should always be set. diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.h b/chromium/content/browser/accessibility/browser_accessibility_win.h index b6599325da8..db8040e3161 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win.h +++ b/chromium/content/browser/accessibility/browser_accessibility_win.h @@ -771,6 +771,7 @@ BrowserAccessibilityWin // Accessors. int32 ia_role() const { return ia_role_; } int32 ia_state() const { return ia_state_; } + const string16& role_name() const { return role_name_; } int32 ia2_role() const { return ia2_role_; } int32 ia2_state() const { return ia2_state_; } const std::vector<string16>& ia2_attributes() const { @@ -816,9 +817,13 @@ BrowserAccessibilityWin void IntAttributeToIA2(AccessibilityNodeData::IntAttribute attribute, const char* ia2_attr); + // Get the value text, which might come from the floating-point + // value for some roles. + string16 GetValueText(); + // Get the text of this node for the purposes of IAccessibleText - it may // be the name, it may be the value, etc. depending on the role. - const string16& TextForIAccessibleText(); + string16 TextForIAccessibleText(); // If offset is a member of IA2TextSpecialOffsets this function updates the // value of offset and returns, otherwise offset remains unchanged. @@ -847,6 +852,7 @@ BrowserAccessibilityWin // IAccessible role and state. int32 ia_role_; int32 ia_state_; + string16 role_name_; // IAccessible2 role and state. int32 ia2_role_; diff --git a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc index 98a5d404a43..c623e257803 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" #include "base/win/scoped_bstr.h" #include "base/win/scoped_comptr.h" #include "base/win/scoped_variant.h" @@ -113,20 +114,20 @@ TEST_F(BrowserAccessibilityTest, TestNoLeaks) { // BrowserAccessibilityManager. AccessibilityNodeData button; button.id = 2; - button.name = L"Button"; - button.role = AccessibilityNodeData::ROLE_BUTTON; + button.SetName("Button"); + button.role = WebKit::WebAXRoleButton; button.state = 0; AccessibilityNodeData checkbox; checkbox.id = 3; - checkbox.name = L"Checkbox"; - checkbox.role = AccessibilityNodeData::ROLE_CHECKBOX; + checkbox.SetName("Checkbox"); + checkbox.role = WebKit::WebAXRoleCheckBox; checkbox.state = 0; AccessibilityNodeData root; root.id = 1; - root.name = L"Document"; - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + root.SetName("Document"); + root.role = WebKit::WebAXRoleRootWebArea; root.state = 0; root.child_ids.push_back(2); root.child_ids.push_back(3); @@ -182,14 +183,14 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) { // BrowserAccessibilityManager. AccessibilityNodeData text; text.id = 2; - text.role = AccessibilityNodeData::ROLE_STATIC_TEXT; - text.name = L"old text"; + text.role = WebKit::WebAXRoleStaticText; + text.SetName("old text"); text.state = 0; AccessibilityNodeData root; root.id = 1; - root.name = L"Document"; - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + root.SetName("Document"); + root.role = WebKit::WebAXRoleRootWebArea; root.state = 0; root.child_ids.push_back(2); @@ -225,14 +226,18 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) { text_accessible.Release(); // Notify the BrowserAccessibilityManager that the text child has changed. - text.name = L"new text"; - AccessibilityHostMsg_NotificationParams param; - param.notification_type = AccessibilityNotificationChildrenChanged; - param.nodes.push_back(text); - param.id = text.id; - std::vector<AccessibilityHostMsg_NotificationParams> notifications; - notifications.push_back(param); - manager->OnAccessibilityNotifications(notifications); + AccessibilityNodeData text2; + text2.id = 2; + text2.role = WebKit::WebAXRoleStaticText; + text2.SetName("new text"); + text2.SetName("old text"); + AccessibilityHostMsg_EventParams param; + param.event_type = WebKit::WebAXEventChildrenChanged; + param.nodes.push_back(text2); + param.id = text2.id; + std::vector<AccessibilityHostMsg_EventParams> events; + events.push_back(param); + manager->OnAccessibilityEvents(events); // Query for the text IAccessible and verify that it now returns "new text" // as its value. @@ -262,17 +267,17 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) { // BrowserAccessibilityManager. AccessibilityNodeData div; div.id = 2; - div.role = AccessibilityNodeData::ROLE_GROUP; + div.role = WebKit::WebAXRoleGroup; div.state = 0; AccessibilityNodeData text3; text3.id = 3; - text3.role = AccessibilityNodeData::ROLE_STATIC_TEXT; + text3.role = WebKit::WebAXRoleStaticText; text3.state = 0; AccessibilityNodeData text4; text4.id = 4; - text4.role = AccessibilityNodeData::ROLE_STATIC_TEXT; + text4.role = WebKit::WebAXRoleStaticText; text4.state = 0; div.child_ids.push_back(3); @@ -280,7 +285,7 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) { AccessibilityNodeData root; root.id = 1; - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + root.role = WebKit::WebAXRoleRootWebArea; root.state = 0; root.child_ids.push_back(2); @@ -298,13 +303,13 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) { // Notify the BrowserAccessibilityManager that the div node and its children // were removed and ensure that only one BrowserAccessibility instance exists. root.child_ids.clear(); - AccessibilityHostMsg_NotificationParams param; - param.notification_type = AccessibilityNotificationChildrenChanged; + AccessibilityHostMsg_EventParams param; + param.event_type = WebKit::WebAXEventChildrenChanged; param.nodes.push_back(root); param.id = root.id; - std::vector<AccessibilityHostMsg_NotificationParams> notifications; - notifications.push_back(param); - manager->OnAccessibilityNotifications(notifications); + std::vector<AccessibilityHostMsg_EventParams> events; + events.push_back(param); + manager->OnAccessibilityEvents(events); ASSERT_EQ(1, CountedBrowserAccessibility::num_instances()); // Delete the manager and test that all BrowserAccessibility instances are @@ -314,16 +319,21 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) { } TEST_F(BrowserAccessibilityTest, TestTextBoundaries) { + std::string text1_value = "One two three.\nFour five six."; + AccessibilityNodeData text1; text1.id = 11; - text1.role = AccessibilityNodeData::ROLE_TEXT_FIELD; + text1.role = WebKit::WebAXRoleTextField; text1.state = 0; - text1.value = L"One two three.\nFour five six."; - text1.line_breaks.push_back(15); + text1.AddStringAttribute(AccessibilityNodeData::ATTR_VALUE, text1_value); + std::vector<int32> line_breaks; + line_breaks.push_back(15); + text1.AddIntListAttribute( + AccessibilityNodeData::ATTR_LINE_BREAKS, line_breaks); AccessibilityNodeData root; root.id = 1; - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + root.role = WebKit::WebAXRoleRootWebArea; root.state = 0; root.child_ids.push_back(11); @@ -344,7 +354,7 @@ TEST_F(BrowserAccessibilityTest, TestTextBoundaries) { base::win::ScopedBstr text; ASSERT_EQ(S_OK, text1_obj->get_text(0, text1_len, text.Receive())); - ASSERT_EQ(text1.value, string16(text)); + ASSERT_EQ(text1_value, base::UTF16ToUTF8(string16(text))); text.Reset(); ASSERT_EQ(S_OK, text1_obj->get_text(0, 4, text.Receive())); @@ -405,22 +415,25 @@ TEST_F(BrowserAccessibilityTest, TestTextBoundaries) { } TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) { + const std::string text1_name = "One two three."; + const std::string text2_name = " Four five six."; + AccessibilityNodeData text1; text1.id = 11; - text1.role = AccessibilityNodeData::ROLE_STATIC_TEXT; - text1.state = 1 << AccessibilityNodeData::STATE_READONLY; - text1.name = L"One two three."; + text1.role = WebKit::WebAXRoleStaticText; + text1.state = 1 << WebKit::WebAXStateReadonly; + text1.SetName(text1_name); AccessibilityNodeData text2; text2.id = 12; - text2.role = AccessibilityNodeData::ROLE_STATIC_TEXT; - text2.state = 1 << AccessibilityNodeData::STATE_READONLY; - text2.name = L" Four five six."; + text2.role = WebKit::WebAXRoleStaticText; + text2.state = 1 << WebKit::WebAXStateReadonly; + text2.SetName(text2_name); AccessibilityNodeData root; root.id = 1; - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; - root.state = 1 << AccessibilityNodeData::STATE_READONLY; + root.role = WebKit::WebAXRoleRootWebArea; + root.state = 1 << WebKit::WebAXStateReadonly; root.child_ids.push_back(11); root.child_ids.push_back(12); @@ -439,7 +452,7 @@ TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) { base::win::ScopedBstr text; ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive())); - EXPECT_EQ(text1.name + text2.name, string16(text)); + EXPECT_EQ(text1_name + text2_name, base::UTF16ToUTF8(string16(text))); long hyperlink_count; ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count)); @@ -468,42 +481,47 @@ TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) { } TEST_F(BrowserAccessibilityTest, TestComplexHypertext) { + const std::string text1_name = "One two three."; + const std::string text2_name = " Four five six."; + const std::string button1_text_name = "red"; + const std::string link1_text_name = "blue"; + AccessibilityNodeData text1; text1.id = 11; - text1.role = AccessibilityNodeData::ROLE_STATIC_TEXT; - text1.state = 1 << AccessibilityNodeData::STATE_READONLY; - text1.name = L"One two three."; + text1.role = WebKit::WebAXRoleStaticText; + text1.state = 1 << WebKit::WebAXStateReadonly; + text1.SetName(text1_name); AccessibilityNodeData text2; text2.id = 12; - text2.role = AccessibilityNodeData::ROLE_STATIC_TEXT; - text2.state = 1 << AccessibilityNodeData::STATE_READONLY; - text2.name = L" Four five six."; + text2.role = WebKit::WebAXRoleStaticText; + text2.state = 1 << WebKit::WebAXStateReadonly; + text2.SetName(text2_name); AccessibilityNodeData button1, button1_text; button1.id = 13; button1_text.id = 15; - button1_text.name = L"red"; - button1.role = AccessibilityNodeData::ROLE_BUTTON; - button1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT; - button1.state = 1 << AccessibilityNodeData::STATE_READONLY; - button1_text.state = 1 << AccessibilityNodeData::STATE_READONLY; + button1_text.SetName(button1_text_name); + button1.role = WebKit::WebAXRoleButton; + button1_text.role = WebKit::WebAXRoleStaticText; + button1.state = 1 << WebKit::WebAXStateReadonly; + button1_text.state = 1 << WebKit::WebAXStateReadonly; button1.child_ids.push_back(15); AccessibilityNodeData link1, link1_text; link1.id = 14; link1_text.id = 16; - link1_text.name = L"blue"; - link1.role = AccessibilityNodeData::ROLE_LINK; - link1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT; - link1.state = 1 << AccessibilityNodeData::STATE_READONLY; - link1_text.state = 1 << AccessibilityNodeData::STATE_READONLY; + link1_text.SetName(link1_text_name); + link1.role = WebKit::WebAXRoleLink; + link1_text.role = WebKit::WebAXRoleStaticText; + link1.state = 1 << WebKit::WebAXStateReadonly; + link1_text.state = 1 << WebKit::WebAXStateReadonly; link1.child_ids.push_back(16); AccessibilityNodeData root; root.id = 1; - root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; - root.state = 1 << AccessibilityNodeData::STATE_READONLY; + root.role = WebKit::WebAXRoleRootWebArea; + root.state = 1 << WebKit::WebAXStateReadonly; root.child_ids.push_back(11); root.child_ids.push_back(13); root.child_ids.push_back(12); @@ -527,8 +545,10 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) { base::win::ScopedBstr text; ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive())); - const string16 embed = BrowserAccessibilityWin::kEmbeddedCharacter; - EXPECT_EQ(text1.name + embed + text2.name + embed, string16(text)); + const std::string embed = base::UTF16ToUTF8( + BrowserAccessibilityWin::kEmbeddedCharacter); + EXPECT_EQ(text1_name + embed + text2_name + embed, + UTF16ToUTF8(string16(text))); text.Reset(); long hyperlink_count; @@ -545,7 +565,8 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) { EXPECT_EQ(S_OK, hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive())); EXPECT_EQ(S_OK, hypertext->get_text(0, 3, text.Receive())); - EXPECT_STREQ(L"red", text); + EXPECT_STREQ(button1_text_name.c_str(), + base::UTF16ToUTF8(string16(text)).c_str()); text.Reset(); hyperlink.Release(); hypertext.Release(); @@ -554,7 +575,8 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) { EXPECT_EQ(S_OK, hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive())); EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive())); - EXPECT_STREQ(L"blue", text); + EXPECT_STREQ(link1_text_name.c_str(), + base::UTF16ToUTF8(string16(text)).c_str()); text.Reset(); hyperlink.Release(); hypertext.Release(); @@ -579,8 +601,9 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) { // Try creating an empty document with busy state. Readonly is // set automatically. CountedBrowserAccessibility::reset(); - const int32 busy_state = 1 << AccessibilityNodeData::STATE_BUSY; - const int32 readonly_state = 1 << AccessibilityNodeData::STATE_READONLY; + const int32 busy_state = 1 << WebKit::WebAXStateBusy; + const int32 readonly_state = 1 << WebKit::WebAXStateReadonly; + const int32 enabled_state = 1 << WebKit::WebAXStateEnabled; scoped_ptr<BrowserAccessibilityManager> manager( new BrowserAccessibilityManagerWin( GetDesktopWindow(), @@ -592,28 +615,28 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) { // Verify the root is as we expect by default. BrowserAccessibility* root = manager->GetRoot(); EXPECT_EQ(0, root->renderer_id()); - EXPECT_EQ(AccessibilityNodeData::ROLE_ROOT_WEB_AREA, root->role()); - EXPECT_EQ(busy_state | readonly_state, root->state()); + EXPECT_EQ(WebKit::WebAXRoleRootWebArea, root->role()); + EXPECT_EQ(busy_state | readonly_state | enabled_state, root->state()); // Tree with a child textfield. AccessibilityNodeData tree1_1; tree1_1.id = 1; - tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + tree1_1.role = WebKit::WebAXRoleRootWebArea; tree1_1.child_ids.push_back(2); AccessibilityNodeData tree1_2; tree1_2.id = 2; - tree1_2.role = AccessibilityNodeData::ROLE_TEXT_FIELD; + tree1_2.role = WebKit::WebAXRoleTextField; // Process a load complete. - std::vector<AccessibilityHostMsg_NotificationParams> params; - params.push_back(AccessibilityHostMsg_NotificationParams()); - AccessibilityHostMsg_NotificationParams* msg = ¶ms[0]; - msg->notification_type = AccessibilityNotificationLoadComplete; + std::vector<AccessibilityHostMsg_EventParams> params; + params.push_back(AccessibilityHostMsg_EventParams()); + AccessibilityHostMsg_EventParams* msg = ¶ms[0]; + msg->event_type = WebKit::WebAXEventLoadComplete; msg->nodes.push_back(tree1_1); msg->nodes.push_back(tree1_2); msg->id = tree1_1.id; - manager->OnAccessibilityNotifications(params); + manager->OnAccessibilityEvents(params); // Save for later comparison. BrowserAccessibility* acc1_2 = manager->GetFromRendererID(2); @@ -622,18 +645,18 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) { EXPECT_NE(root, manager->GetRoot()); // And the proper child remains. - EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, acc1_2->role()); + EXPECT_EQ(WebKit::WebAXRoleTextField, acc1_2->role()); EXPECT_EQ(2, acc1_2->renderer_id()); // Tree with a child button. AccessibilityNodeData tree2_1; tree2_1.id = 1; - tree2_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; + tree2_1.role = WebKit::WebAXRoleRootWebArea; tree2_1.child_ids.push_back(3); AccessibilityNodeData tree2_2; tree2_2.id = 3; - tree2_2.role = AccessibilityNodeData::ROLE_BUTTON; + tree2_2.role = WebKit::WebAXRoleButton; msg->nodes.clear(); msg->nodes.push_back(tree2_1); @@ -641,7 +664,7 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) { msg->id = tree2_1.id; // Fire another load complete. - manager->OnAccessibilityNotifications(params); + manager->OnAccessibilityEvents(params); BrowserAccessibility* acc2_2 = manager->GetFromRendererID(3); @@ -649,7 +672,7 @@ TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) { EXPECT_NE(root, manager->GetRoot()); // And the new child exists. - EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, acc2_2->role()); + EXPECT_EQ(WebKit::WebAXRoleButton, acc2_2->role()); EXPECT_EQ(3, acc2_2->renderer_id()); // Ensure we properly cleaned up. diff --git a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc index 5e3effe3c2a..eb61a1c8d24 100644 --- a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc +++ b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc @@ -10,7 +10,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_widget_host_view.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/accessibility_browser_test_utils.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" @@ -40,7 +40,7 @@ class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest { const AccessibilityNodeDataTreeNode& GetAccessibilityNodeDataTree( AccessibilityMode accessibility_mode = AccessibilityModeComplete) { AccessibilityNotificationWaiter waiter( - shell(), accessibility_mode, AccessibilityNotificationLayoutComplete); + shell(), accessibility_mode, WebKit::WebAXEventLayoutComplete); waiter.WaitForNotification(); return waiter.GetAccessibilityNodeDataTree(); } @@ -89,16 +89,15 @@ CrossPlatformAccessibilityBrowserTest::TearDownInProcessBrowserTestFixture() { } // Convenience method to get the value of a particular AccessibilityNodeData -// node attribute as a UTF-8 const char*. +// node attribute as a UTF-8 string. std::string CrossPlatformAccessibilityBrowserTest::GetAttr( const AccessibilityNodeData& node, const AccessibilityNodeData::StringAttribute attr) { - std::map<AccessibilityNodeData::StringAttribute, string16>::const_iterator - iter = node.string_attributes.find(attr); - if (iter != node.string_attributes.end()) - return UTF16ToUTF8(iter->second); - else - return std::string(); + for (size_t i = 0; i < node.string_attributes.size(); ++i) { + if (node.string_attributes[i].first == attr) + return node.string_attributes[i].second; + } + return std::string(); } // Convenience method to get the value of a particular AccessibilityNodeData @@ -106,12 +105,11 @@ std::string CrossPlatformAccessibilityBrowserTest::GetAttr( int CrossPlatformAccessibilityBrowserTest::GetIntAttr( const AccessibilityNodeData& node, const AccessibilityNodeData::IntAttribute attr) { - std::map<AccessibilityNodeData::IntAttribute, int32>::const_iterator iter = - node.int_attributes.find(attr); - if (iter != node.int_attributes.end()) - return iter->second; - else - return -1; + for (size_t i = 0; i < node.int_attributes.size(); ++i) { + if (node.int_attributes[i].first == attr) + return node.int_attributes[i].second; + } + return -1; } // Convenience method to get the value of a particular AccessibilityNodeData @@ -119,12 +117,11 @@ int CrossPlatformAccessibilityBrowserTest::GetIntAttr( bool CrossPlatformAccessibilityBrowserTest::GetBoolAttr( const AccessibilityNodeData& node, const AccessibilityNodeData::BoolAttribute attr) { - std::map<AccessibilityNodeData::BoolAttribute, bool>::const_iterator iter = - node.bool_attributes.find(attr); - if (iter != node.bool_attributes.end()) - return iter->second; - else - return false; + for (size_t i = 0; i < node.bool_attributes.size(); ++i) { + if (node.bool_attributes[i].first == attr) + return node.bool_attributes[i].second; + } + return false; } // Marked flaky per http://crbug.com/101984 @@ -152,13 +149,15 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, EXPECT_STREQ( "text/html", GetAttr(tree, AccessibilityNodeData::ATTR_DOC_MIMETYPE).c_str()); - EXPECT_STREQ("Accessibility Test", UTF16ToUTF8(tree.name).c_str()); - EXPECT_EQ(AccessibilityNodeData::ROLE_ROOT_WEB_AREA, tree.role); + EXPECT_STREQ( + "Accessibility Test", + GetAttr(tree, AccessibilityNodeData::ATTR_NAME).c_str()); + EXPECT_EQ(WebKit::WebAXRoleRootWebArea, tree.role); // Check properites of the BODY element. ASSERT_EQ(1U, tree.children.size()); const AccessibilityNodeDataTreeNode& body = tree.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_GROUP, body.role); + EXPECT_EQ(WebKit::WebAXRoleGroup, body.role); EXPECT_STREQ("body", GetAttr(body, AccessibilityNodeData::ATTR_HTML_TAG).c_str()); EXPECT_STREQ("block", @@ -168,31 +167,31 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, ASSERT_EQ(2U, body.children.size()); const AccessibilityNodeDataTreeNode& button = body.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button.role); + EXPECT_EQ(WebKit::WebAXRoleButton, button.role); EXPECT_STREQ( "input", GetAttr(button, AccessibilityNodeData::ATTR_HTML_TAG).c_str()); - EXPECT_STREQ("push", UTF16ToUTF8(button.name).c_str()); + EXPECT_STREQ( + "push", + GetAttr(button, AccessibilityNodeData::ATTR_NAME).c_str()); EXPECT_STREQ( "inline-block", GetAttr(button, AccessibilityNodeData::ATTR_DISPLAY).c_str()); ASSERT_EQ(2U, button.html_attributes.size()); - EXPECT_STREQ("type", UTF16ToUTF8(button.html_attributes[0].first).c_str()); - EXPECT_STREQ("button", UTF16ToUTF8(button.html_attributes[0].second).c_str()); - EXPECT_STREQ("value", UTF16ToUTF8(button.html_attributes[1].first).c_str()); - EXPECT_STREQ("push", UTF16ToUTF8(button.html_attributes[1].second).c_str()); + EXPECT_STREQ("type", button.html_attributes[0].first.c_str()); + EXPECT_STREQ("button", button.html_attributes[0].second.c_str()); + EXPECT_STREQ("value", button.html_attributes[1].first.c_str()); + EXPECT_STREQ("push", button.html_attributes[1].second.c_str()); const AccessibilityNodeDataTreeNode& checkbox = body.children[1]; - EXPECT_EQ(AccessibilityNodeData::ROLE_CHECKBOX, checkbox.role); + EXPECT_EQ(WebKit::WebAXRoleCheckBox, checkbox.role); EXPECT_STREQ( "input", GetAttr(checkbox, AccessibilityNodeData::ATTR_HTML_TAG).c_str()); EXPECT_STREQ( "inline-block", GetAttr(checkbox, AccessibilityNodeData::ATTR_DISPLAY).c_str()); ASSERT_EQ(1U, checkbox.html_attributes.size()); - EXPECT_STREQ( - "type", UTF16ToUTF8(checkbox.html_attributes[0].first).c_str()); - EXPECT_STREQ( - "checkbox", UTF16ToUTF8(checkbox.html_attributes[0].second).c_str()); + EXPECT_STREQ("type", checkbox.html_attributes[0].first.c_str()); + EXPECT_STREQ("checkbox", checkbox.html_attributes[0].second.c_str()); } IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, @@ -212,12 +211,14 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, const AccessibilityNodeDataTreeNode& body = tree.children[0]; ASSERT_EQ(1U, body.children.size()); const AccessibilityNodeDataTreeNode& text = body.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, text.role); + EXPECT_EQ(WebKit::WebAXRoleTextField, text.role); EXPECT_STREQ( "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str()); EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START)); EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END)); - EXPECT_STREQ("Hello, world.", UTF16ToUTF8(text.value).c_str()); + EXPECT_STREQ( + "Hello, world.", + GetAttr(text, AccessibilityNodeData::ATTR_VALUE).c_str()); // TODO(dmazzoni): as soon as more accessibility code is cross-platform, // this code should test that the accessible info is dynamically updated @@ -241,12 +242,14 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, const AccessibilityNodeDataTreeNode& body = tree.children[0]; ASSERT_EQ(1U, body.children.size()); const AccessibilityNodeDataTreeNode& text = body.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, text.role); + EXPECT_EQ(WebKit::WebAXRoleTextField, text.role); EXPECT_STREQ( "input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str()); EXPECT_EQ(0, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_START)); EXPECT_EQ(13, GetIntAttr(text, AccessibilityNodeData::ATTR_TEXT_SEL_END)); - EXPECT_STREQ("Hello, world.", UTF16ToUTF8(text.value).c_str()); + EXPECT_STREQ( + "Hello, world.", + GetAttr(text, AccessibilityNodeData::ATTR_VALUE).c_str()); } IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, @@ -266,23 +269,32 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree(); ASSERT_EQ(1U, tree.children.size()); const AccessibilityNodeDataTreeNode& table = tree.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_TABLE, table.role); + EXPECT_EQ(WebKit::WebAXRoleTable, table.role); const AccessibilityNodeDataTreeNode& row = table.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, row.role); + EXPECT_EQ(WebKit::WebAXRoleRow, row.role); const AccessibilityNodeDataTreeNode& cell1 = row.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_CELL, cell1.role); + EXPECT_EQ(WebKit::WebAXRoleCell, cell1.role); const AccessibilityNodeDataTreeNode& cell2 = row.children[1]; - EXPECT_EQ(AccessibilityNodeData::ROLE_CELL, cell2.role); + EXPECT_EQ(WebKit::WebAXRoleCell, cell2.role); const AccessibilityNodeDataTreeNode& column1 = table.children[1]; - EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, column1.role); + EXPECT_EQ(WebKit::WebAXRoleColumn, column1.role); EXPECT_EQ(0U, column1.children.size()); - EXPECT_EQ(1U, column1.indirect_child_ids.size()); - EXPECT_EQ(cell1.id, column1.indirect_child_ids[0]); + EXPECT_EQ(1U, column1.intlist_attributes.size()); + EXPECT_EQ(AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS, + column1.intlist_attributes[0].first); + const std::vector<int32> column1_indirect_child_ids = + column1.intlist_attributes[0].second; + EXPECT_EQ(1U, column1_indirect_child_ids.size()); + EXPECT_EQ(cell1.id, column1_indirect_child_ids[0]); const AccessibilityNodeDataTreeNode& column2 = table.children[2]; - EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, column2.role); + EXPECT_EQ(WebKit::WebAXRoleColumn, column2.role); EXPECT_EQ(0U, column2.children.size()); - EXPECT_EQ(1U, column2.indirect_child_ids.size()); - EXPECT_EQ(cell2.id, column2.indirect_child_ids[0]); + EXPECT_EQ(AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS, + column2.intlist_attributes[0].first); + const std::vector<int32> column2_indirect_child_ids = + column2.intlist_attributes[0].second; + EXPECT_EQ(1U, column2_indirect_child_ids.size()); + EXPECT_EQ(cell2.id, column2_indirect_child_ids[0]); } IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, @@ -328,8 +340,10 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, ASSERT_EQ(3U, body.children.size()); const AccessibilityNodeDataTreeNode& button1 = body.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button1.role); - EXPECT_STREQ("Button 1", UTF16ToUTF8(button1.name).c_str()); + EXPECT_EQ(WebKit::WebAXRoleButton, button1.role); + EXPECT_STREQ( + "Button 1", + GetAttr(button1, AccessibilityNodeData::ATTR_NAME).c_str()); const AccessibilityNodeDataTreeNode& iframe = body.children[1]; EXPECT_STREQ("iframe", @@ -337,23 +351,25 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, ASSERT_EQ(1U, iframe.children.size()); const AccessibilityNodeDataTreeNode& scroll_area = iframe.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_SCROLLAREA, scroll_area.role); + EXPECT_EQ(WebKit::WebAXRoleScrollArea, scroll_area.role); ASSERT_EQ(1U, scroll_area.children.size()); const AccessibilityNodeDataTreeNode& sub_document = scroll_area.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_WEB_AREA, sub_document.role); + EXPECT_EQ(WebKit::WebAXRoleWebArea, sub_document.role); ASSERT_EQ(1U, sub_document.children.size()); const AccessibilityNodeDataTreeNode& sub_body = sub_document.children[0]; ASSERT_EQ(1U, sub_body.children.size()); const AccessibilityNodeDataTreeNode& button2 = sub_body.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button2.role); - EXPECT_STREQ("Button 2", UTF16ToUTF8(button2.name).c_str()); + EXPECT_EQ(WebKit::WebAXRoleButton, button2.role); + EXPECT_STREQ("Button 2", + GetAttr(button2, AccessibilityNodeData::ATTR_NAME).c_str()); const AccessibilityNodeDataTreeNode& button3 = body.children[2]; - EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button3.role); - EXPECT_STREQ("Button 3", UTF16ToUTF8(button3.name).c_str()); + EXPECT_EQ(WebKit::WebAXRoleButton, button3.role); + EXPECT_STREQ("Button 3", + GetAttr(button3, AccessibilityNodeData::ATTR_NAME).c_str()); } IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, @@ -397,13 +413,13 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree(); const AccessibilityNodeDataTreeNode& table = tree.children[0]; - EXPECT_EQ(AccessibilityNodeData::ROLE_TABLE, table.role); + EXPECT_EQ(WebKit::WebAXRoleTable, table.role); ASSERT_GE(table.children.size(), 5U); - EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, table.children[0].role); - EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, table.children[1].role); - EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, table.children[2].role); - EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, table.children[3].role); - EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, table.children[4].role); + EXPECT_EQ(WebKit::WebAXRoleRow, table.children[0].role); + EXPECT_EQ(WebKit::WebAXRoleRow, table.children[1].role); + EXPECT_EQ(WebKit::WebAXRoleColumn, table.children[2].role); + EXPECT_EQ(WebKit::WebAXRoleColumn, table.children[3].role); + EXPECT_EQ(WebKit::WebAXRoleColumn, table.children[4].role); EXPECT_EQ(3, GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT)); EXPECT_EQ(2, GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_ROW_COUNT)); @@ -413,13 +429,17 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, const AccessibilityNodeDataTreeNode& cell3 = table.children[1].children[0]; const AccessibilityNodeDataTreeNode& cell4 = table.children[1].children[1]; - ASSERT_EQ(6U, table.cell_ids.size()); - EXPECT_EQ(cell1.id, table.cell_ids[0]); - EXPECT_EQ(cell1.id, table.cell_ids[1]); - EXPECT_EQ(cell2.id, table.cell_ids[2]); - EXPECT_EQ(cell3.id, table.cell_ids[3]); - EXPECT_EQ(cell4.id, table.cell_ids[4]); - EXPECT_EQ(cell4.id, table.cell_ids[5]); + ASSERT_EQ(AccessibilityNodeData::ATTR_CELL_IDS, + table.intlist_attributes[0].first); + const std::vector<int32>& table_cell_ids = + table.intlist_attributes[0].second; + ASSERT_EQ(6U, table_cell_ids.size()); + EXPECT_EQ(cell1.id, table_cell_ids[0]); + EXPECT_EQ(cell1.id, table_cell_ids[1]); + EXPECT_EQ(cell2.id, table_cell_ids[2]); + EXPECT_EQ(cell3.id, table_cell_ids[3]); + EXPECT_EQ(cell4.id, table_cell_ids[4]); + EXPECT_EQ(cell4.id, table_cell_ids[5]); EXPECT_EQ(0, GetIntAttr(cell1, AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX)); diff --git a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index d0ff119fd24..948b8fde628 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc @@ -21,7 +21,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/content_paths.h" #include "content/public/common/url_constants.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/accessibility_browser_test_utils.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" @@ -140,14 +140,14 @@ void DumpAccessibilityTreeTest::RunTest( printf("Testing: %s\n", html_file.MaybeAsASCII().c_str()); std::string html_contents; - file_util::ReadFileToString(html_file, &html_contents); + base::ReadFileToString(html_file, &html_contents); // Read the expected file. std::string expected_contents_raw; base::FilePath expected_file = base::FilePath(html_file.RemoveExtension().value() + AccessibilityTreeFormatter::GetExpectedFileSuffix()); - file_util::ReadFileToString(expected_file, &expected_contents_raw); + base::ReadFileToString(expected_file, &expected_contents_raw); // Tolerate Windows-style line endings (\r\n) in the expected file: // normalize by deleting all \r from the file (if any) to leave only \n. @@ -166,7 +166,7 @@ void DumpAccessibilityTreeTest::RunTest( html_file.BaseName().MaybeAsASCII().c_str()); AccessibilityNotificationWaiter waiter( shell(), AccessibilityModeComplete, - AccessibilityNotificationLoadComplete); + WebKit::WebAXEventLoadComplete); NavigateToURL(shell(), url); waiter.WaitForNotification(); diff --git a/chromium/content/browser/android/OWNERS b/chromium/content/browser/android/OWNERS index 6084aca7150..a5cf4a90179 100644 --- a/chromium/content/browser/android/OWNERS +++ b/chromium/content/browser/android/OWNERS @@ -3,6 +3,3 @@ joth@chromium.org tedchoc@chromium.org yfriedman@chromium.org sievers@chromium.org - -# per-file rules: -per-file *media*=qinmin@chromium.org diff --git a/chromium/content/browser/android/android_browser_process.cc b/chromium/content/browser/android/android_browser_process.cc deleted file mode 100644 index edc48e62619..00000000000 --- a/chromium/content/browser/android/android_browser_process.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2012 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/browser/android/android_browser_process.h" - -#include "base/android/jni_string.h" -#include "base/debug/debugger.h" -#include "base/logging.h" -#include "content/browser/android/content_startup_flags.h" -#include "content/public/common/content_constants.h" -#include "jni/AndroidBrowserProcess_jni.h" - -using base::android::ConvertJavaStringToUTF8; - -namespace content { - -static void SetCommandLineFlags(JNIEnv*env, - jclass clazz, - jint max_render_process_count, - jstring plugin_descriptor) { - std::string plugin_str = (plugin_descriptor == NULL ? - std::string() : ConvertJavaStringToUTF8(env, plugin_descriptor)); - SetContentCommandLineFlags(max_render_process_count, plugin_str); -} - -static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) { -#if defined(OFFICIAL_BUILD) - return true; -#else - return false; -#endif -} - -static jboolean IsPluginEnabled(JNIEnv* env, jclass clazz) { -#if defined(ENABLE_PLUGINS) - return true; -#else - return false; -#endif -} - -bool RegisterAndroidBrowserProcess(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -} // namespace diff --git a/chromium/content/browser/android/android_browser_process.h b/chromium/content/browser/android/android_browser_process.h deleted file mode 100644 index fbd06e822b7..00000000000 --- a/chromium/content/browser/android/android_browser_process.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2012 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_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_ -#define CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_ - -#include <jni.h> - -namespace content { - -bool RegisterAndroidBrowserProcess(JNIEnv* env); - -} // namespace content - -#endif // CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_ diff --git a/chromium/content/browser/android/browser_jni_registrar.cc b/chromium/content/browser/android/browser_jni_registrar.cc deleted file mode 100644 index 35da3af3f3f..00000000000 --- a/chromium/content/browser/android/browser_jni_registrar.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2012 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/browser/android/browser_jni_registrar.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_registrar.h" -#include "content/browser/accessibility/browser_accessibility_android.h" -#include "content/browser/accessibility/browser_accessibility_manager_android.h" -#include "content/browser/android/android_browser_process.h" -#include "content/browser/android/browser_startup_config.h" -#include "content/browser/android/child_process_launcher_android.h" -#include "content/browser/android/content_settings.h" -#include "content/browser/android/content_video_view.h" -#include "content/browser/android/content_view_core_impl.h" -#include "content/browser/android/content_view_render_view.h" -#include "content/browser/android/content_view_statics.h" -#include "content/browser/android/date_time_chooser_android.h" -#include "content/browser/android/download_controller_android_impl.h" -#include "content/browser/android/interstitial_page_delegate_android.h" -#include "content/browser/android/load_url_params.h" -#include "content/browser/android/media_resource_getter_impl.h" -#include "content/browser/android/surface_texture_peer_browser_impl.h" -#include "content/browser/android/touch_point.h" -#include "content/browser/android/tracing_intent_handler.h" -#include "content/browser/android/vibration_message_filter.h" -#include "content/browser/android/web_contents_observer_android.h" -#include "content/browser/device_orientation/data_fetcher_impl_android.h" -#include "content/browser/geolocation/location_api_adapter_android.h" -#include "content/browser/power_save_blocker_android.h" -#include "content/browser/renderer_host/ime_adapter_android.h" -#include "content/browser/renderer_host/java/java_bound_object.h" -#include "content/browser/speech/speech_recognizer_impl_android.h" - -using content::SurfaceTexturePeerBrowserImpl; - -namespace { -base::android::RegistrationMethod kContentRegisteredMethods[] = { - {"AndroidLocationApiAdapter", - content::AndroidLocationApiAdapter::RegisterGeolocationService}, - {"AndroidBrowserProcess", content::RegisterAndroidBrowserProcess}, - {"BrowserAccessibilityManager", - content::RegisterBrowserAccessibilityManager}, - {"BrowserStartupConfiguration", content::RegisterBrowserStartupConfig}, - {"ChildProcessLauncher", content::RegisterChildProcessLauncher}, - {"ContentSettings", content::ContentSettings::RegisterContentSettings}, - {"ContentViewRenderView", - content::ContentViewRenderView::RegisterContentViewRenderView}, - {"ContentVideoView", content::ContentVideoView::RegisterContentVideoView}, - {"ContentViewCore", content::RegisterContentViewCore}, - {"DataFetcherImplAndroid", content::DataFetcherImplAndroid::Register}, - {"DateTimePickerAndroid", content::RegisterDateTimeChooserAndroid}, - {"DownloadControllerAndroidImpl", - content::DownloadControllerAndroidImpl::RegisterDownloadController}, - {"InterstitialPageDelegateAndroid", - content::InterstitialPageDelegateAndroid:: - RegisterInterstitialPageDelegateAndroid}, - {"MediaResourceGetterImpl", - content::MediaResourceGetterImpl::RegisterMediaResourceGetter}, - {"LoadUrlParams", content::RegisterLoadUrlParams}, - {"PowerSaveBlock", content::RegisterPowerSaveBlocker}, - {"RegisterImeAdapter", content::RegisterImeAdapter}, - {"SpeechRecognizerImplAndroid", - content::SpeechRecognizerImplAndroid::RegisterSpeechRecognizer}, - {"TouchPoint", content::RegisterTouchPoint}, - {"TracingIntentHandler", content::RegisterTracingIntentHandler}, - {"VibrationMessageFilter", content::VibrationMessageFilter::Register}, - {"WebContentsObserverAndroid", content::RegisterWebContentsObserverAndroid}, - {"WebViewStatics", content::RegisterWebViewStatics}, }; - -} // namespace - -namespace content { -namespace android { - -bool RegisterBrowserJni(JNIEnv* env) { - return RegisterNativeMethods(env, kContentRegisteredMethods, - arraysize(kContentRegisteredMethods)); -} - -} // namespace android -} // namespace content diff --git a/chromium/content/browser/android/browser_jni_registrar.h b/chromium/content/browser/android/browser_jni_registrar.h deleted file mode 100644 index 15f4fb34e10..00000000000 --- a/chromium/content/browser/android/browser_jni_registrar.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2012 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_BROWSER_ANDROID_BROWSER_JNI_REGISTRAR_H_ -#define CONTENT_BROWSER_ANDROID_BROWSER_JNI_REGISTRAR_H_ - -#include <jni.h> - -#include "content/common/content_export.h" - -namespace content { -namespace android { - -// Register all JNI bindings necessary for content browser. -CONTENT_EXPORT bool RegisterBrowserJni(JNIEnv* env); - -} // namespace android -} // namespace content - -#endif // CONTENT_BROWSER_ANDROID_BROWSER_JNI_REGISTRAR_H_ diff --git a/chromium/content/browser/android/browser_startup_config.cc b/chromium/content/browser/android/browser_startup_config.cc deleted file mode 100644 index 3e742080695..00000000000 --- a/chromium/content/browser/android/browser_startup_config.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2013 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/browser/android/browser_startup_config.h" - -#include "base/android/jni_android.h" -#include "jni/BrowserStartupConfig_jni.h" - -namespace content { - -bool BrowserMayStartAsynchronously() { - JNIEnv* env = base::android::AttachCurrentThread(); - return Java_BrowserStartupConfig_browserMayStartAsynchonously(env); -} - -void BrowserStartupComplete(int result) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_BrowserStartupConfig_browserStartupComplete(env, result); -} - -bool RegisterBrowserStartupConfig(JNIEnv* env) { - return RegisterNativesImpl(env); -} -} // namespace content diff --git a/chromium/content/browser/android/browser_startup_controller.cc b/chromium/content/browser/android/browser_startup_controller.cc new file mode 100644 index 00000000000..502f198edba --- /dev/null +++ b/chromium/content/browser/android/browser_startup_controller.cc @@ -0,0 +1,56 @@ +// Copyright 2013 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/browser/android/browser_startup_controller.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "content/browser/android/content_startup_flags.h" + +#include "jni/BrowserStartupController_jni.h" + +namespace content { + +bool BrowserMayStartAsynchronously() { + JNIEnv* env = base::android::AttachCurrentThread(); + return Java_BrowserStartupController_browserMayStartAsynchonously(env); +} + +void BrowserStartupComplete(int result) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_BrowserStartupController_browserStartupComplete(env, result); +} + +bool RegisterBrowserStartupController(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +static void SetCommandLineFlags(JNIEnv* env, + jclass clazz, + jint max_render_process_count, + jstring plugin_descriptor) { + std::string plugin_str = + (plugin_descriptor == NULL + ? std::string() + : base::android::ConvertJavaStringToUTF8(env, plugin_descriptor)); + SetContentCommandLineFlags(max_render_process_count, plugin_str); +} + +static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) { +#if defined(OFFICIAL_BUILD) + return true; +#else + return false; +#endif +} + +static jboolean IsPluginEnabled(JNIEnv* env, jclass clazz) { +#if defined(ENABLE_PLUGINS) + return true; +#else + return false; +#endif +} + +} // namespace content diff --git a/chromium/content/browser/android/browser_startup_config.h b/chromium/content/browser/android/browser_startup_controller.h index 95575f2bc2d..6fa334e8f3a 100644 --- a/chromium/content/browser/android/browser_startup_config.h +++ b/chromium/content/browser/android/browser_startup_controller.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_BROWSER_STARTUP_CONFIGURATION_H_ -#define CONTENT_BROWSER_BROWSER_STARTUP_CONFIGURATION_H_ +#ifndef CONTENT_BROWSER_BROWSER_STARTUP_CONTROLLER_H_ +#define CONTENT_BROWSER_BROWSER_STARTUP_CONTROLLER_H_ #include <jni.h> @@ -12,7 +12,7 @@ namespace content { bool BrowserMayStartAsynchronously(); void BrowserStartupComplete(int result); -bool RegisterBrowserStartupConfig(JNIEnv* env); +bool RegisterBrowserStartupController(JNIEnv* env); } // namespace content -#endif // CONTENT_BROWSER_BROWSER_STARTUP_CONFIGURATION_H_ +#endif // CONTENT_BROWSER_BROWSER_STARTUP_CONTROLLER_H_ diff --git a/chromium/content/browser/android/child_process_launcher_android.cc b/chromium/content/browser/android/child_process_launcher_android.cc index d73e2992112..962fee861f7 100644 --- a/chromium/content/browser/android/child_process_launcher_android.cc +++ b/chromium/content/browser/android/child_process_launcher_android.cc @@ -8,6 +8,7 @@ #include "base/android/jni_array.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/renderer_host/compositor_impl_android.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/public/browser/browser_thread.h" @@ -15,7 +16,6 @@ #include "content/public/common/content_switches.h" #include "jni/ChildProcessLauncher_jni.h" #include "media/base/android/media_player_android.h" -#include "media/base/android/media_player_manager.h" #include "ui/gl/android/scoped_java_surface.h" using base::android::AttachCurrentThread; diff --git a/chromium/content/browser/android/content_startup_flags.cc b/chromium/content/browser/android/content_startup_flags.cc index 065b3e70b3f..b755e4dc783 100644 --- a/chromium/content/browser/android/content_startup_flags.cc +++ b/chromium/content/browser/android/content_startup_flags.cc @@ -58,9 +58,12 @@ void SetContentCommandLineFlags(int max_render_process_count, parsed_command_line->AppendSwitch( switches::kEnableCompositedScrollingForFrames); parsed_command_line->AppendSwitch(switches::kEnableBeginFrameScheduling); + parsed_command_line->AppendSwitch(switches::kEnableDeadlineScheduling); parsed_command_line->AppendSwitch(switches::kEnableGestureTapHighlight); parsed_command_line->AppendSwitch(switches::kEnablePinch); + parsed_command_line->AppendSwitch(switches::kEnableOverlayFullscreenVideo); + parsed_command_line->AppendSwitch(switches::kEnableOverlayScrollbars); parsed_command_line->AppendSwitch(switches::kEnableOverscrollNotifications); // Run the GPU service as a thread in the browser instead of as a diff --git a/chromium/content/browser/android/content_video_view.cc b/chromium/content/browser/android/content_video_view.cc index 4a0fdc67a44..b8d5c64d542 100644 --- a/chromium/content/browser/android/content_video_view.cc +++ b/chromium/content/browser/android/content_video_view.cc @@ -6,7 +6,7 @@ #include "base/command_line.h" #include "base/logging.h" -#include "content/browser/android/browser_media_player_manager.h" +#include "content/browser/media/android/browser_media_player_manager.h" #include "content/common/android/surface_texture_peer.h" #include "content/public/common/content_switches.h" #include "jni/ContentVideoView_jni.h" diff --git a/chromium/content/browser/android/content_view_core_impl.cc b/chromium/content/browser/android/content_view_core_impl.cc index 33b3f068d2a..dd15c1f67b8 100644 --- a/chromium/content/browser/android/content_view_core_impl.cc +++ b/chromium/content/browser/android/content_view_core_impl.cc @@ -15,10 +15,10 @@ #include "base/values.h" #include "cc/layers/layer.h" #include "cc/output/begin_frame_args.h" -#include "content/browser/android/browser_media_player_manager.h" #include "content/browser/android/interstitial_page_delegate_android.h" #include "content/browser/android/load_url_params.h" #include "content/browser/android/touch_point.h" +#include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/renderer_host/compositor_impl_android.h" #include "content/browser/renderer_host/input/web_input_event_builders_android.h" #include "content/browser/renderer_host/java/java_bound_object.h" @@ -260,6 +260,7 @@ void ContentViewCoreImpl::Observe(int type, env, obj.obj(), old_pid, new_pid); } } + SetFocusInternal(HasFocus()); break; } case NOTIFICATION_RENDERER_PROCESS_CREATED: { @@ -341,6 +342,13 @@ void ContentViewCoreImpl::Show() { void ContentViewCoreImpl::Hide() { GetWebContents()->WasHidden(); + PauseVideo(); +} + +void ContentViewCoreImpl::PauseVideo() { + RenderViewHost* host = web_contents_->GetRenderViewHost(); + if (host) + host->Send(new ViewMsg_PauseVideo(host->GetRoutingID())); } void ContentViewCoreImpl::OnTabCrashed() { @@ -375,6 +383,10 @@ void ContentViewCoreImpl::UpdateFrameInfo( const gfx::Vector2dF& controls_offset, const gfx::Vector2dF& content_offset, float overdraw_bottom_height) { + if (window_android_) + window_android_->set_content_offset( + gfx::ScaleVector2d(content_offset, dpi_scale_)); + JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) @@ -577,15 +589,28 @@ void ContentViewCoreImpl::ShowDisambiguationPopup( java_bitmap.obj()); } -ScopedJavaLocalRef<jobject> ContentViewCoreImpl::CreateSmoothScroller( - bool scroll_down, int mouse_event_x, int mouse_event_y) { +ScopedJavaLocalRef<jobject> ContentViewCoreImpl::CreateOnePointTouchGesture( + int32 start_x, int32 start_y, int32 delta_x, int32 delta_y) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) return ScopedJavaLocalRef<jobject>(); - return Java_ContentViewCore_createSmoothScroller( - env, obj.obj(), scroll_down, mouse_event_x, mouse_event_y); + return Java_ContentViewCore_createOnePointTouchGesture( + env, obj.obj(), start_x, start_y, delta_x, delta_y); +} + +ScopedJavaLocalRef<jobject> ContentViewCoreImpl::CreateTwoPointTouchGesture( + int32 start_x0, int32 start_y0, int32 delta_x0, int32 delta_y0, + int32 start_x1, int32 start_y1, int32 delta_x1, int32 delta_y1) { + JNIEnv* env = AttachCurrentThread(); + + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return ScopedJavaLocalRef<jobject>(); + return Java_ContentViewCore_createTwoPointTouchGesture( + env, obj.obj(), start_x0, start_y0, delta_x0, delta_y0, + start_x1, start_y1, delta_x1, delta_y1); } void ContentViewCoreImpl::NotifyExternalSurface( @@ -817,6 +842,10 @@ WebContents* ContentViewCoreImpl::GetWebContents() const { } void ContentViewCoreImpl::SetFocus(JNIEnv* env, jobject obj, jboolean focused) { + SetFocusInternal(focused); +} + +void ContentViewCoreImpl::SetFocusInternal(bool focused) { if (!GetRenderWidgetHostViewAndroid()) return; @@ -947,20 +976,13 @@ void ContentViewCoreImpl::ScrollEnd(JNIEnv* env, jobject obj, jlong time_ms) { } void ContentViewCoreImpl::ScrollBy(JNIEnv* env, jobject obj, jlong time_ms, - jfloat x, jfloat y, jfloat dx, jfloat dy, - jboolean last_input_event_for_vsync) { + jfloat x, jfloat y, jfloat dx, jfloat dy) { WebGestureEvent event = MakeGestureEvent( WebInputEvent::GestureScrollUpdate, time_ms, x, y); event.data.scrollUpdate.deltaX = -dx / GetDpiScale(); event.data.scrollUpdate.deltaY = -dy / GetDpiScale(); SendGestureEvent(event); - - // TODO(brianderson): Clean up last_input_event_for_vsync. crbug.com/247043 - if (last_input_event_for_vsync) { - SendBeginFrame(base::TimeTicks() + - base::TimeDelta::FromMilliseconds(time_ms)); - } } void ContentViewCoreImpl::FlingStart(JNIEnv* env, jobject obj, jlong time_ms, @@ -1083,19 +1105,12 @@ void ContentViewCoreImpl::PinchEnd(JNIEnv* env, jobject obj, jlong time_ms) { void ContentViewCoreImpl::PinchBy(JNIEnv* env, jobject obj, jlong time_ms, jfloat anchor_x, jfloat anchor_y, - jfloat delta, - jboolean last_input_event_for_vsync) { + jfloat delta) { WebGestureEvent event = MakeGestureEvent( WebInputEvent::GesturePinchUpdate, time_ms, anchor_x, anchor_y); event.data.pinchUpdate.scale = delta; SendGestureEvent(event); - - // TODO(brianderson): Clean up last_input_event_for_vsync. crbug.com/247043 - if (last_input_event_for_vsync) { - SendBeginFrame(base::TimeTicks() + - base::TimeDelta::FromMilliseconds(time_ms)); - } } void ContentViewCoreImpl::SelectBetweenCoordinates(JNIEnv* env, jobject obj, @@ -1151,6 +1166,15 @@ void ContentViewCoreImpl::GoToNavigationIndex(JNIEnv* env, UpdateTabCrashedFlag(); } +void ContentViewCoreImpl::LoadIfNecessary(JNIEnv* env, jobject obj) { + web_contents_->GetController().LoadIfNecessary(); + UpdateTabCrashedFlag(); +} + +void ContentViewCoreImpl::RequestRestoreLoad(JNIEnv* env, jobject obj) { + web_contents_->GetController().SetNeedsReload(); +} + void ContentViewCoreImpl::StopLoading(JNIEnv* env, jobject obj) { web_contents_->Stop(); } @@ -1296,12 +1320,8 @@ void ContentViewCoreImpl::AttachExternalVideoSurface(JNIEnv* env, #if defined(GOOGLE_TV) RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>( web_contents_->GetRenderViewHost()); - BrowserMediaPlayerManager* browser_media_player_manager = - rvhi ? static_cast<BrowserMediaPlayerManager*>( - rvhi->media_player_manager()) - : NULL; - if (browser_media_player_manager) { - browser_media_player_manager->AttachExternalVideoSurface( + if (rvhi && rvhi->media_player_manager()) { + rvhi->media_player_manager()->AttachExternalVideoSurface( static_cast<int>(player_id), jsurface); } #endif @@ -1313,12 +1333,8 @@ void ContentViewCoreImpl::DetachExternalVideoSurface(JNIEnv* env, #if defined(GOOGLE_TV) RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>( web_contents_->GetRenderViewHost()); - BrowserMediaPlayerManager* browser_media_player_manager = - rvhi ? static_cast<BrowserMediaPlayerManager*>( - rvhi->media_player_manager()) - : NULL; - if (browser_media_player_manager) { - browser_media_player_manager->DetachExternalVideoSurface( + if (rvhi && rvhi->media_player_manager()) { + rvhi->media_player_manager()->DetachExternalVideoSurface( static_cast<int>(player_id)); } #endif @@ -1332,6 +1348,8 @@ jboolean ContentViewCoreImpl::IsRenderWidgetHostViewReady(JNIEnv* env, void ContentViewCoreImpl::ExitFullscreen(JNIEnv* env, jobject obj) { RenderViewHost* host = web_contents_->GetRenderViewHost(); + if (!host) + return; host->ExitFullscreen(); } @@ -1341,6 +1359,8 @@ void ContentViewCoreImpl::UpdateTopControlsState(JNIEnv* env, bool enable_showing, bool animate) { RenderViewHost* host = web_contents_->GetRenderViewHost(); + if (!host) + return; host->Send(new ViewMsg_UpdateTopControlsState(host->GetRoutingID(), enable_hiding, enable_showing, @@ -1574,17 +1594,12 @@ void ContentViewCoreImpl::SetAccessibilityEnabled(JNIEnv* env, jobject obj, return; RenderWidgetHostImpl* host_impl = RenderWidgetHostImpl::From( host_view->GetRenderWidgetHost()); - BrowserAccessibilityState* accessibility_state = - BrowserAccessibilityState::GetInstance(); if (enabled) { - // This enables accessibility globally unless it was explicitly disallowed - // by a command-line flag. - accessibility_state->OnScreenReaderDetected(); - // If it was actually enabled globally, enable it for this RenderWidget now. - if (accessibility_state->IsAccessibleBrowser() && host_impl) + BrowserAccessibilityState::GetInstance()->EnableAccessibility(); + if (host_impl) host_impl->SetAccessibilityMode(AccessibilityModeComplete); } else { - accessibility_state->DisableAccessibility(); + BrowserAccessibilityState::GetInstance()->DisableAccessibility(); if (host_impl) host_impl->SetAccessibilityMode(AccessibilityModeOff); } diff --git a/chromium/content/browser/android/content_view_core_impl.h b/chromium/content/browser/android/content_view_core_impl.h index f3cf755257a..765f63da983 100644 --- a/chromium/content/browser/android/content_view_core_impl.h +++ b/chromium/content/browser/android/content_view_core_impl.h @@ -60,6 +60,7 @@ class ContentViewCoreImpl : public ContentViewCore, virtual float GetDpiScale() const OVERRIDE; virtual void RequestContentClipping(const gfx::Rect& clipping, const gfx::Size& content_size) OVERRIDE; + virtual void PauseVideo() OVERRIDE; // -------------------------------------------------------------------------- // Methods called from Java via JNI @@ -107,8 +108,7 @@ class ContentViewCoreImpl : public ContentViewCore, void ScrollBegin(JNIEnv* env, jobject obj, jlong time_ms, jfloat x, jfloat y); void ScrollEnd(JNIEnv* env, jobject obj, jlong time_ms); void ScrollBy(JNIEnv* env, jobject obj, jlong time_ms, - jfloat x, jfloat y, jfloat dx, jfloat dy, - jboolean last_input_event_for_vsync); + jfloat x, jfloat y, jfloat dx, jfloat dy); void FlingStart(JNIEnv* env, jobject obj, jlong time_ms, jfloat x, jfloat y, jfloat vx, jfloat vy); void FlingCancel(JNIEnv* env, jobject obj, jlong time_ms); @@ -132,8 +132,7 @@ class ContentViewCoreImpl : public ContentViewCore, void PinchBegin(JNIEnv* env, jobject obj, jlong time_ms, jfloat x, jfloat y); void PinchEnd(JNIEnv* env, jobject obj, jlong time_ms); void PinchBy(JNIEnv* env, jobject obj, jlong time_ms, - jfloat x, jfloat y, jfloat delta, - jboolean last_input_event_for_vsync); + jfloat x, jfloat y, jfloat delta); void SelectBetweenCoordinates(JNIEnv* env, jobject obj, jfloat x1, jfloat y1, jfloat x2, jfloat y2); @@ -146,6 +145,8 @@ class ContentViewCoreImpl : public ContentViewCore, void GoForward(JNIEnv* env, jobject obj); void GoToOffset(JNIEnv* env, jobject obj, jint offset); void GoToNavigationIndex(JNIEnv* env, jobject obj, jint index); + void LoadIfNecessary(JNIEnv* env, jobject obj); + void RequestRestoreLoad(JNIEnv* env, jobject obj); void StopLoading(JNIEnv* env, jobject obj); void Reload(JNIEnv* env, jobject obj); void CancelPendingReload(JNIEnv* env, jobject obj); @@ -268,10 +269,16 @@ class ContentViewCoreImpl : public ContentViewCore, void ShowDisambiguationPopup( const gfx::Rect& target_rect, const SkBitmap& zoomed_bitmap); - // Creates a java-side smooth scroller. Used by + // Creates a java-side touch gesture, e.g. used by // chrome.gpuBenchmarking.smoothScrollBy. - base::android::ScopedJavaLocalRef<jobject> CreateSmoothScroller( - bool scroll_down, int mouse_event_x, int mouse_event_y); + base::android::ScopedJavaLocalRef<jobject> CreateOnePointTouchGesture( + int start_x, int start_y, int delta_x, int delta_y); + + // Creates a java-side touch gesture with two pointers, e.g. used by + // chrome.gpuBenchmarking.pinchBy. + base::android::ScopedJavaLocalRef<jobject> CreateTwoPointTouchGesture( + int start_x0, int start_y0, int delta_x0, int delta_y0, + int start_x1, int start_y1, int delta_x1, int delta_y1); // Notifies the java object about the external surface, requesting for one if // necessary. @@ -335,6 +342,9 @@ class ContentViewCoreImpl : public ContentViewCore, // |tab_crashed_| accordingly. void UpdateTabCrashedFlag(); + // Update focus state of the RenderWidgetHostView. + void SetFocusInternal(bool focused); + // A weak reference to the Java ContentViewCore object. JavaObjectWeakGlobalRef java_ref_; diff --git a/chromium/content/browser/android/content_view_render_view.cc b/chromium/content/browser/android/content_view_render_view.cc index 37a24aa91c8..4faa4df97aa 100644 --- a/chromium/content/browser/android/content_view_render_view.cc +++ b/chromium/content/browser/android/content_view_render_view.cc @@ -29,18 +29,18 @@ bool ContentViewRenderView::RegisterContentViewRenderView(JNIEnv* env) { return RegisterNativesImpl(env); } -ContentViewRenderView::ContentViewRenderView() - : scheduled_composite_(false), - weak_factory_(this) { +ContentViewRenderView::ContentViewRenderView(JNIEnv* env, jobject obj) + : buffers_swapped_during_composite_(false) { + java_obj_.Reset(env, obj); } ContentViewRenderView::~ContentViewRenderView() { } // static -jint Init(JNIEnv* env, jclass clazz) { +static jint Init(JNIEnv* env, jobject obj) { ContentViewRenderView* content_view_render_view = - new ContentViewRenderView(); + new ContentViewRenderView(env, obj); return reinterpret_cast<jint>(content_view_render_view); } @@ -55,6 +55,8 @@ void ContentViewRenderView::SetCurrentContentView( reinterpret_cast<ContentViewCoreImpl*>(native_content_view); if (content_view) compositor_->SetRootLayer(content_view->GetLayer()); + else + compositor_->SetRootLayer(cc::Layer::Create()); } void ContentViewRenderView::SurfaceCreated( @@ -72,28 +74,32 @@ void ContentViewRenderView::SurfaceSetSize( compositor_->SetWindowBounds(gfx::Size(width, height)); } +jboolean ContentViewRenderView::Composite(JNIEnv* env, jobject obj) { + if (!compositor_) + return false; + + buffers_swapped_during_composite_ = false; + compositor_->Composite(); + return buffers_swapped_during_composite_; +} + void ContentViewRenderView::ScheduleComposite() { - if (scheduled_composite_) - return; - - scheduled_composite_ = true; - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&ContentViewRenderView::Composite, - weak_factory_.GetWeakPtr())); + JNIEnv* env = base::android::AttachCurrentThread(); + Java_ContentViewRenderView_requestRender(env, java_obj_.obj()); } -void ContentViewRenderView::InitCompositor() { - if (!compositor_) - compositor_.reset(Compositor::Create(this)); +void ContentViewRenderView::OnSwapBuffersPosted() { + buffers_swapped_during_composite_ = true; } -void ContentViewRenderView::Composite() { - if (!compositor_) - return; +void ContentViewRenderView::OnSwapBuffersCompleted() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_ContentViewRenderView_onSwapBuffersCompleted(env, java_obj_.obj()); +} - scheduled_composite_ = false; - compositor_->Composite(); +void ContentViewRenderView::InitCompositor() { + if (!compositor_) + compositor_.reset(Compositor::Create(this)); } } // namespace content diff --git a/chromium/content/browser/android/content_view_render_view.h b/chromium/content/browser/android/content_view_render_view.h index dbaeccf7514..714f02f7d32 100644 --- a/chromium/content/browser/android/content_view_render_view.h +++ b/chromium/content/browser/android/content_view_render_view.h @@ -5,8 +5,7 @@ #ifndef CONTENT_BROWSER_ANDROID_CONTENT_VIEW_RENDER_VIEW_H_ #define CONTENT_BROWSER_ANDROID_CONTENT_VIEW_RENDER_VIEW_H_ -#include <jni.h> - +#include "base/android/jni_helper.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -20,35 +19,31 @@ class ContentViewRenderView : public CompositorClient { // Registers the JNI methods for ContentViewRender. static bool RegisterContentViewRenderView(JNIEnv* env); - ContentViewRenderView(); + ContentViewRenderView(JNIEnv* env, jobject obj); - // -------------------------------------------------------------------------- - // Methods called from Java via JNI - // -------------------------------------------------------------------------- - static jint Init(JNIEnv* env, jclass clazz); + // Methods called from Java via JNI ----------------------------------------- void Destroy(JNIEnv* env, jobject obj); void SetCurrentContentView(JNIEnv* env, jobject obj, int native_content_view); void SurfaceCreated(JNIEnv* env, jobject obj, jobject jsurface); void SurfaceDestroyed(JNIEnv* env, jobject obj); void SurfaceSetSize(JNIEnv* env, jobject obj, jint width, jint height); + jboolean Composite(JNIEnv* env, jobject obj); + + // CompositorClient --------------------------------------------------------- + virtual void ScheduleComposite() OVERRIDE; + virtual void OnSwapBuffersPosted() OVERRIDE; + virtual void OnSwapBuffersCompleted() OVERRIDE; private: - friend class base::RefCounted<ContentViewRenderView>; virtual ~ContentViewRenderView(); - // CompositorClient implementation. - virtual void ScheduleComposite() OVERRIDE; - void InitCompositor(); - void Composite(); - scoped_ptr<content::Compositor> compositor_; - bool scheduled_composite_; + bool buffers_swapped_during_composite_; - base::WeakPtrFactory<ContentViewRenderView> weak_factory_; + base::android::ScopedJavaGlobalRef<jobject> java_obj_; - // Note that this class does not call back to Java and as a result does not - // have a reference to its Java object. + scoped_ptr<content::Compositor> compositor_; DISALLOW_COPY_AND_ASSIGN(ContentViewRenderView); }; diff --git a/chromium/content/browser/android/date_time_chooser_android.cc b/chromium/content/browser/android/date_time_chooser_android.cc index 0c3ba2d7327..7c91ff0977d 100644 --- a/chromium/content/browser/android/date_time_chooser_android.cc +++ b/chromium/content/browser/android/date_time_chooser_android.cc @@ -24,7 +24,14 @@ class DateTimeChooserAndroid::DateTimeIPCSender : explicit DateTimeIPCSender(RenderViewHost* sender); virtual ~DateTimeIPCSender() {} void ReplaceDateTime(int dialog_type, - int year, int month, int day, int hour, int minute, int second, int week); + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week); void CancelDialog(); private: @@ -36,9 +43,15 @@ DateTimeChooserAndroid::DateTimeIPCSender::DateTimeIPCSender( : RenderViewHostObserver(sender) { } -void DateTimeChooserAndroid::DateTimeIPCSender::ReplaceDateTime( - int dialog_type, - int year, int month, int day, int hour, int minute, int second, int week) { +void DateTimeChooserAndroid::DateTimeIPCSender::ReplaceDateTime(int dialog_type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week) { ViewHostMsg_DateTimeDialogValue_Params value; value.year = year; value.month = month; @@ -46,6 +59,7 @@ void DateTimeChooserAndroid::DateTimeIPCSender::ReplaceDateTime( value.hour = hour; value.minute = minute; value.second = second; + value.milli = milli; value.week = week; value.dialog_type = dialog_type; Send(new ViewMsg_ReplaceDateTime(routing_id(), value)); @@ -76,30 +90,60 @@ void DateTimeChooserAndroid::InitializeDateInputTypes( text_input_type_time, text_input_type_week); } -void DateTimeChooserAndroid::ReplaceDateTime( - JNIEnv* env, jobject, int dialog_type, - int year, int month, int day, int hour, int minute, int second, int week) { +void DateTimeChooserAndroid::ReplaceDateTime(JNIEnv* env, + jobject, + int dialog_type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week) { sender_->ReplaceDateTime( - dialog_type, year, month, day, hour, minute, second, week); + dialog_type, year, month, day, hour, minute, second, milli, week); } void DateTimeChooserAndroid::CancelDialog(JNIEnv* env, jobject) { sender_->CancelDialog(); } -void DateTimeChooserAndroid::ShowDialog( - ContentViewCore* content, RenderViewHost* sender, - int type, int year, int month, int day, - int hour, int minute, int second, int week, double min, double max) { +void DateTimeChooserAndroid::ShowDialog(ContentViewCore* content, + RenderViewHost* sender, + int type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week, + double min, + double max, + double step) { if (sender_) delete sender_; sender_ = new DateTimeIPCSender(sender); JNIEnv* env = AttachCurrentThread(); j_date_time_chooser_.Reset(Java_DateTimeChooserAndroid_createDateTimeChooser( - env, content->GetJavaObject().obj(), + env, + content->GetJavaObject().obj(), reinterpret_cast<intptr_t>(this), - type, year, month, day, hour, minute, second, week, min, max)); + type, + year, + month, + day, + hour, + minute, + second, + milli, + week, + min, + max, + step)); } // ---------------------------------------------------------------------------- diff --git a/chromium/content/browser/android/date_time_chooser_android.h b/chromium/content/browser/android/date_time_chooser_android.h index 646ba3c14ea..68cb4560172 100644 --- a/chromium/content/browser/android/date_time_chooser_android.h +++ b/chromium/content/browser/android/date_time_chooser_android.h @@ -24,14 +24,31 @@ class DateTimeChooserAndroid { // DateTimeChooser implementation: void ShowDialog(ContentViewCore* content, RenderViewHost* sender, - int type, int year, int month, int day, - int hour, int minute, int second, int week, - double min, double max); + int type, + int year, + int month, + int day, + int hour, + int minute, + int second, + int milli, + int week, + double min, + double max, + double step); // Replaces the current value with the one passed the different fields - void ReplaceDateTime(JNIEnv* env, jobject, jint dialog_type, - jint year, jint month, jint day, - jint hour, jint minute, jint second, jint week); + void ReplaceDateTime(JNIEnv* env, + jobject, + jint dialog_type, + jint year, + jint month, + jint day, + jint hour, + jint minute, + jint second, + jint milli, + jint week); // Closes the dialog without propagating any changes. void CancelDialog(JNIEnv* env, jobject); diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc b/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc index 5ab105b9a70..b04e85456f1 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc +++ b/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc @@ -16,13 +16,20 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/renderer/android/synchronous_compositor_factory.h" +#include "content/renderer/media/android/stream_texture_factory_android_synchronous_impl.h" +#include "gpu/command_buffer/client/gl_in_process_context.h" +#include "gpu/command_buffer/service/stream_texture_manager_in_process_android.h" +#include "ui/gl/android/surface_texture.h" #include "ui/gl/gl_surface.h" #include "webkit/common/gpu/context_provider_in_process.h" +#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" namespace content { namespace { +using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; + int GetInProcessRendererId() { content::RenderProcessHost::iterator it = content::RenderProcessHost::AllHostsIterator(); @@ -38,9 +45,39 @@ int GetInProcessRendererId() { return id; } +class VideoContextProvider + : public StreamTextureFactorySynchronousImpl::ContextProvider { + public: + VideoContextProvider( + const scoped_refptr<cc::ContextProvider>& context_provider, + gpu::GLInProcessContext* gl_in_process_context) + : context_provider_(context_provider), + gl_in_process_context_(gl_in_process_context) {} + + virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture( + uint32 stream_id) OVERRIDE { + return gl_in_process_context_->GetSurfaceTexture(stream_id); + } + + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + return context_provider_->Context3d(); + } + + private: + friend class base::RefCountedThreadSafe<VideoContextProvider>; + virtual ~VideoContextProvider() {} + + scoped_refptr<cc::ContextProvider> context_provider_; + gpu::GLInProcessContext* gl_in_process_context_; + + DISALLOW_COPY_AND_ASSIGN(VideoContextProvider); +}; + class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory { public: - SynchronousCompositorFactoryImpl() { + SynchronousCompositorFactoryImpl() + : wrapped_gl_context_for_main_thread_(NULL), + num_hardware_compositors_(0) { SynchronousCompositorFactory::SetInstance(this); } @@ -65,15 +102,59 @@ class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory { return &synchronous_input_event_filter_; } + scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl> + CreateOffscreenContext() { + if (!gfx::GLSurface::InitializeOneOff()) + return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>(); + + const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; + + WebKit::WebGraphicsContext3D::Attributes attributes; + attributes.antialias = false; + attributes.shareResources = true; + attributes.noAutomaticFlushes = true; + + gpu::GLInProcessContextAttribs in_process_attribs; + WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes( + attributes, &in_process_attribs); + scoped_ptr<gpu::GLInProcessContext> context( + gpu::GLInProcessContext::CreateContext(true, + NULL, + gfx::Size(1, 1), + attributes.shareResources, + in_process_attribs, + gpu_preference)); + + wrapped_gl_context_for_main_thread_ = context.get(); + if (!context.get()) + return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>(); + + return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>( + WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext( + context.Pass(), attributes)); + } + virtual scoped_refptr<cc::ContextProvider> GetOffscreenContextProviderForMainThread() OVERRIDE { - if (!offscreen_context_for_main_thread_.get() || - offscreen_context_for_main_thread_->DestroyedOnMainThread()) { + // This check only guarantees the main thread context is created after + // a compositor did successfully initialize hardware draw in the past. + // In particular this does not guarantee that the main thread context + // will fail creation when all compositors release hardware draw. + bool failed = !CanCreateMainThreadContext(); + if (!failed && + (!offscreen_context_for_main_thread_.get() || + offscreen_context_for_main_thread_->DestroyedOnMainThread())) { offscreen_context_for_main_thread_ = - webkit::gpu::ContextProviderInProcess::Create(); - if (offscreen_context_for_main_thread_.get() && - !offscreen_context_for_main_thread_->BindToCurrentThread()) - offscreen_context_for_main_thread_ = NULL; + webkit::gpu::ContextProviderInProcess::Create( + CreateOffscreenContext(), + "Compositor-Offscreen"); + failed = !offscreen_context_for_main_thread_.get() || + !offscreen_context_for_main_thread_->BindToCurrentThread(); + } + + if (failed) { + offscreen_context_for_main_thread_ = NULL; + wrapped_gl_context_for_main_thread_ = NULL; } return offscreen_context_for_main_thread_; } @@ -86,25 +167,86 @@ class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory { // any thread and is lightweight. virtual scoped_refptr<cc::ContextProvider> GetOffscreenContextProviderForCompositorThread() OVERRIDE { - base::AutoLock lock(offscreen_context_for_compositor_thread_creation_lock_); + base::AutoLock lock(offscreen_context_for_compositor_thread_lock_); if (!offscreen_context_for_compositor_thread_.get() || offscreen_context_for_compositor_thread_->DestroyedOnMainThread()) { offscreen_context_for_compositor_thread_ = - webkit::gpu::ContextProviderInProcess::Create(); + webkit::gpu::ContextProviderInProcess::CreateOffscreen(); } return offscreen_context_for_compositor_thread_; } + virtual scoped_ptr<StreamTextureFactory> CreateStreamTextureFactory( + int view_id) OVERRIDE { + scoped_refptr<VideoContextProvider> context_provider; + if (CanCreateMainThreadContext()) { + context_provider = + new VideoContextProvider(offscreen_context_for_main_thread_, + wrapped_gl_context_for_main_thread_); + } + return make_scoped_ptr(new StreamTextureFactorySynchronousImpl( + context_provider.get(), view_id)) + .PassAs<StreamTextureFactory>(); + } + + void CompositorInitializedHardwareDraw(SynchronousCompositorImpl* compositor); + void CompositorReleasedHardwareDraw(SynchronousCompositorImpl* compositor); + private: + void ReleaseGlobalHardwareResources(); + bool CanCreateMainThreadContext(); + SynchronousInputEventFilter synchronous_input_event_filter_; - // Only guards construction of |offscreen_context_for_compositor_thread_|, - // not usage. - base::Lock offscreen_context_for_compositor_thread_creation_lock_; + // Only guards construction and destruction of + // |offscreen_context_for_compositor_thread_|, not usage. + base::Lock offscreen_context_for_compositor_thread_lock_; scoped_refptr<cc::ContextProvider> offscreen_context_for_main_thread_; + // This is a pointer to the context owned by + // |offscreen_context_for_main_thread_|. + gpu::GLInProcessContext* wrapped_gl_context_for_main_thread_; scoped_refptr<cc::ContextProvider> offscreen_context_for_compositor_thread_; + + // |num_hardware_compositor_lock_| is updated on UI thread only but can be + // read on renderer main thread. + base::Lock num_hardware_compositor_lock_; + unsigned int num_hardware_compositors_; }; +void SynchronousCompositorFactoryImpl::CompositorInitializedHardwareDraw( + SynchronousCompositorImpl* compositor) { + base::AutoLock lock(num_hardware_compositor_lock_); + num_hardware_compositors_++; +} + +void SynchronousCompositorFactoryImpl::CompositorReleasedHardwareDraw( + SynchronousCompositorImpl* compositor) { + bool should_release_resources = false; + { + base::AutoLock lock(num_hardware_compositor_lock_); + DCHECK_GT(num_hardware_compositors_, 0u); + num_hardware_compositors_--; + should_release_resources = num_hardware_compositors_ == 0u; + } + if (should_release_resources) + ReleaseGlobalHardwareResources(); +} + +void SynchronousCompositorFactoryImpl::ReleaseGlobalHardwareResources() { + { + base::AutoLock lock(offscreen_context_for_compositor_thread_lock_); + offscreen_context_for_compositor_thread_ = NULL; + } + + // TODO(boliu): Properly clean up command buffer server of main thread + // context here. +} + +bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() { + base::AutoLock lock(num_hardware_compositor_lock_); + return num_hardware_compositors_ > 0; +} + base::LazyInstance<SynchronousCompositorFactoryImpl>::Leaky g_factory = LAZY_INSTANCE_INITIALIZER; @@ -155,27 +297,32 @@ bool SynchronousCompositorImpl::InitializeHwDraw( scoped_refptr<gfx::GLSurface> surface) { DCHECK(CalledOnValidThread()); DCHECK(output_surface_); - return output_surface_->InitializeHwDraw( + bool success = output_surface_->InitializeHwDraw( surface, g_factory.Get().GetOffscreenContextProviderForCompositorThread()); + if (success) + g_factory.Get().CompositorInitializedHardwareDraw(this); + return success; } void SynchronousCompositorImpl::ReleaseHwDraw() { DCHECK(CalledOnValidThread()); DCHECK(output_surface_); - return output_surface_->ReleaseHwDraw(); + output_surface_->ReleaseHwDraw(); + g_factory.Get().CompositorReleasedHardwareDraw(this); } bool SynchronousCompositorImpl::DemandDrawHw( - gfx::Size view_size, + gfx::Size surface_size, const gfx::Transform& transform, - gfx::Rect damage_area, + gfx::Rect viewport, + gfx::Rect clip, bool stencil_enabled) { DCHECK(CalledOnValidThread()); DCHECK(output_surface_); return output_surface_->DemandDrawHw( - view_size, transform, damage_area, stencil_enabled); + surface_size, transform, viewport, clip, stencil_enabled); } bool SynchronousCompositorImpl::DemandDrawSw(SkCanvas* canvas) { @@ -185,6 +332,14 @@ bool SynchronousCompositorImpl::DemandDrawSw(SkCanvas* canvas) { return output_surface_->DemandDrawSw(canvas); } +void SynchronousCompositorImpl::SetMemoryPolicy( + const SynchronousCompositorMemoryPolicy& policy) { + DCHECK(CalledOnValidThread()); + DCHECK(output_surface_); + + return output_surface_->SetMemoryPolicy(policy); +} + void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset() { if (input_handler_) input_handler_->OnRootLayerDelegatedScrollOffsetChanged(); diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_impl.h b/chromium/content/browser/android/in_process/synchronous_compositor_impl.h index f0ea2c6e824..3189dcc983a 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_impl.h +++ b/chromium/content/browser/android/in_process/synchronous_compositor_impl.h @@ -52,11 +52,14 @@ class SynchronousCompositorImpl scoped_refptr<gfx::GLSurface> surface) OVERRIDE; virtual void ReleaseHwDraw() OVERRIDE; virtual bool DemandDrawHw( - gfx::Size view_size, + gfx::Size surface_size, const gfx::Transform& transform, + gfx::Rect viewport, gfx::Rect clip, bool stencil_enabled) OVERRIDE; virtual bool DemandDrawSw(SkCanvas* canvas) OVERRIDE; + virtual void SetMemoryPolicy( + const SynchronousCompositorMemoryPolicy& policy) OVERRIDE; virtual void DidChangeRootLayerScrollOffset() OVERRIDE; // SynchronousCompositorOutputSurfaceDelegate diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc index a6115e98994..28119a20329 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc +++ b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.cc @@ -9,33 +9,30 @@ #include "cc/output/begin_frame_args.h" #include "cc/output/compositor_frame.h" #include "cc/output/context_provider.h" -#include "cc/output/managed_memory_policy.h" #include "cc/output/output_surface_client.h" #include "cc/output/software_output_device.h" #include "content/browser/android/in_process/synchronous_compositor_impl.h" -#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" #include "content/public/browser/browser_thread.h" #include "gpu/command_buffer/client/gl_in_process_context.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkDevice.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/skia_util.h" #include "ui/gfx/transform.h" #include "ui/gl/gl_surface.h" +#include "webkit/common/gpu/context_provider_in_process.h" #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" - namespace content { namespace { -scoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D( - scoped_refptr<gfx::GLSurface> surface) { +scoped_ptr<webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl> +CreateWebGraphicsContext3D(scoped_refptr<gfx::GLSurface> surface) { using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; if (!gfx::GLSurface::InitializeOneOff()) - return scoped_ptr<WebKit::WebGraphicsContext3D>(); + return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>(); - const char* allowed_extensions = "*"; const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; WebKit::WebGraphicsContext3D::Attributes attributes; @@ -49,16 +46,14 @@ scoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D( scoped_ptr<gpu::GLInProcessContext> context( gpu::GLInProcessContext::CreateWithSurface(surface, attributes.shareResources, - allowed_extensions, in_process_attribs, gpu_preference)); if (!context.get()) - return scoped_ptr<WebKit::WebGraphicsContext3D>(); + return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>(); - return scoped_ptr<WebKit::WebGraphicsContext3D>( - WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext( - context.Pass(), attributes)); + return WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext( + context.Pass(), attributes).Pass(); } void DidActivatePendingTree(int routing_id) { @@ -98,7 +93,7 @@ class SynchronousCompositorOutputSurface::SoftwareDevice private: SynchronousCompositorOutputSurface* surface_; - SkDevice null_device_; + SkBitmapDevice null_device_; SkCanvas null_canvas_; DISALLOW_COPY_AND_ASSIGN(SoftwareDevice); @@ -112,7 +107,9 @@ SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface( needs_begin_frame_(false), invoking_composite_(false), did_swap_buffer_(false), - current_sw_canvas_(NULL) { + current_sw_canvas_(NULL), + memory_policy_(0), + output_surface_client_(NULL) { capabilities_.deferred_gl_initialization = true; capabilities_.draw_and_swap_full_viewport_every_frame = true; capabilities_.adjust_deadline_for_parent = false; @@ -140,21 +137,16 @@ bool SynchronousCompositorOutputSurface::BindToClient( DCHECK(CalledOnValidThread()); if (!cc::OutputSurface::BindToClient(surface_client)) return false; - surface_client->SetTreeActivationCallback( + + output_surface_client_ = surface_client; + output_surface_client_->SetTreeActivationCallback( base::Bind(&DidActivatePendingTree, routing_id_)); + output_surface_client_->SetMemoryPolicy(memory_policy_); + SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); if (delegate) delegate->DidBindOutputSurface(this); - const int bytes_limit = 64 * 1024 * 1024; - const int num_resources_limit = 100; - surface_client->SetMemoryPolicy( - cc::ManagedMemoryPolicy(bytes_limit, - cc::ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, - 0, - cc::ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING, - num_resources_limit)); - return true; } @@ -175,9 +167,10 @@ void SynchronousCompositorOutputSurface::SetNeedsBeginFrame( void SynchronousCompositorOutputSurface::SwapBuffers( cc::CompositorFrame* frame) { + DCHECK(CalledOnValidThread()); if (!ForcedDrawToSoftwareDevice()) { - DCHECK(context3d()); - context3d()->shallowFlushCHROMIUM(); + DCHECK(context_provider_); + context_provider_->Context3d()->shallowFlushCHROMIUM(); } SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); if (delegate) @@ -188,44 +181,45 @@ void SynchronousCompositorOutputSurface::SwapBuffers( } namespace { -void AdjustTransformForClip(gfx::Transform* transform, gfx::Rect clip) { - // The system-provided transform translates us from the screen origin to the - // origin of the clip rect, but CC's draw origin starts at the clip. - transform->matrix().postTranslate(-clip.x(), -clip.y(), 0); +void AdjustTransform(gfx::Transform* transform, gfx::Rect viewport) { + // CC's draw origin starts at the viewport. + transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0); } } // namespace bool SynchronousCompositorOutputSurface::InitializeHwDraw( scoped_refptr<gfx::GLSurface> surface, - scoped_refptr<cc::ContextProvider> offscreen_context) { + scoped_refptr<cc::ContextProvider> offscreen_context_provider) { DCHECK(CalledOnValidThread()); DCHECK(HasClient()); - DCHECK(!context3d_); + DCHECK(!context_provider_); DCHECK(surface); - return InitializeAndSetContext3D( - CreateWebGraphicsContext3D(surface).Pass(), offscreen_context); + scoped_refptr<cc::ContextProvider> onscreen_context_provider = + webkit::gpu::ContextProviderInProcess::Create( + CreateWebGraphicsContext3D(surface), "SynchronousCompositor"); + return InitializeAndSetContext3d(onscreen_context_provider, + offscreen_context_provider); } void SynchronousCompositorOutputSurface::ReleaseHwDraw() { + DCHECK(CalledOnValidThread()); cc::OutputSurface::ReleaseGL(); } bool SynchronousCompositorOutputSurface::DemandDrawHw( gfx::Size surface_size, const gfx::Transform& transform, + gfx::Rect viewport, gfx::Rect clip, bool stencil_enabled) { DCHECK(CalledOnValidThread()); DCHECK(HasClient()); - DCHECK(context3d()); + DCHECK(context_provider_); - gfx::Transform adjusted_transform = transform; - AdjustTransformForClip(&adjusted_transform, clip); surface_size_ = surface_size; - SetExternalDrawConstraints(adjusted_transform, clip); SetExternalStencilTest(stencil_enabled); - InvokeComposite(clip.size()); + InvokeComposite(transform, viewport, clip, true); return did_swap_buffer_; } @@ -242,29 +236,48 @@ bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) { gfx::Transform transform(gfx::Transform::kSkipInitialization); transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4. - AdjustTransformForClip(&transform, clip); surface_size_ = gfx::Size(canvas->getDeviceSize().width(), canvas->getDeviceSize().height()); - SetExternalDrawConstraints(transform, clip); SetExternalStencilTest(false); - InvokeComposite(clip.size()); + InvokeComposite(transform, clip, clip, false); return did_swap_buffer_; } void SynchronousCompositorOutputSurface::InvokeComposite( - gfx::Size damage_size) { + const gfx::Transform& transform, + gfx::Rect viewport, + gfx::Rect clip, + bool valid_for_tile_management) { DCHECK(!invoking_composite_); base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true); did_swap_buffer_ = false; - SetNeedsRedrawRect(gfx::Rect(damage_size)); + + gfx::Transform adjusted_transform = transform; + AdjustTransform(&adjusted_transform, viewport); + SetExternalDrawConstraints( + adjusted_transform, viewport, clip, valid_for_tile_management); + SetNeedsRedrawRect(gfx::Rect(viewport.size())); + if (needs_begin_frame_) BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor()); + // After software draws (which might move the viewport arbitrarily), restore + // the previous hardware viewport to allow CC's tile manager to prioritize + // properly. + if (valid_for_tile_management) { + cached_hw_transform_ = adjusted_transform; + cached_hw_viewport_ = viewport; + cached_hw_clip_ = clip; + } else { + SetExternalDrawConstraints( + cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true); + } + if (did_swap_buffer_) - OnSwapBuffersComplete(NULL); + OnSwapBuffersComplete(); } void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() { @@ -272,6 +285,16 @@ void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() { // intentionally no-op here. } +void SynchronousCompositorOutputSurface::SetMemoryPolicy( + const SynchronousCompositorMemoryPolicy& policy) { + DCHECK(CalledOnValidThread()); + memory_policy_.bytes_limit_when_visible = policy.bytes_limit; + memory_policy_.num_resources_limit = policy.num_resources_limit; + + if (output_surface_client_) + output_surface_client_->SetMemoryPolicy(memory_policy_); +} + // Not using base::NonThreadSafe as we want to enforce a more exacting threading // requirement: SynchronousCompositorOutputSurface() must only be used on the UI // thread. diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h index 3ab149818ae..b9b4d678c09 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h +++ b/chromium/content/browser/android/in_process/synchronous_compositor_output_surface.h @@ -9,8 +9,10 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "cc/output/managed_memory_policy.h" #include "cc/output/output_surface.h" #include "content/public/browser/android/synchronous_compositor.h" +#include "ui/gfx/transform.h" namespace cc { class ContextProvider; @@ -63,13 +65,15 @@ class SynchronousCompositorOutputSurface // Partial SynchronousCompositor API implementation. bool InitializeHwDraw( scoped_refptr<gfx::GLSurface> surface, - scoped_refptr<cc::ContextProvider> offscreen_context); + scoped_refptr<cc::ContextProvider> offscreen_context_provider); void ReleaseHwDraw(); bool DemandDrawHw(gfx::Size surface_size, const gfx::Transform& transform, + gfx::Rect viewport, gfx::Rect clip, bool stencil_enabled); bool DemandDrawSw(SkCanvas* canvas); + void SetMemoryPolicy(const SynchronousCompositorMemoryPolicy& policy); private: class SoftwareDevice; @@ -78,7 +82,10 @@ class SynchronousCompositorOutputSurface // Private OutputSurface overrides. virtual void PostCheckForRetroactiveBeginFrame() OVERRIDE; - void InvokeComposite(gfx::Size damage_size); + void InvokeComposite(const gfx::Transform& transform, + gfx::Rect viewport, + gfx::Rect clip, + bool valid_for_tile_management); bool CalledOnValidThread() const; SynchronousCompositorOutputSurfaceDelegate* GetDelegate(); @@ -87,9 +94,17 @@ class SynchronousCompositorOutputSurface bool invoking_composite_; bool did_swap_buffer_; + gfx::Transform cached_hw_transform_; + gfx::Rect cached_hw_viewport_; + gfx::Rect cached_hw_clip_; + // Only valid (non-NULL) during a DemandDrawSw() call. SkCanvas* current_sw_canvas_; + cc::ManagedMemoryPolicy memory_policy_; + + cc::OutputSurfaceClient* output_surface_client_; + DISALLOW_COPY_AND_ASSIGN(SynchronousCompositorOutputSurface); }; diff --git a/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc b/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc index 17c909d42d9..34144dac579 100644 --- a/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc +++ b/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc @@ -8,7 +8,7 @@ #include "cc/input/input_handler.h" #include "content/browser/android/in_process/synchronous_compositor_impl.h" #include "content/public/browser/browser_thread.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" using WebKit::WebInputEvent; diff --git a/chromium/content/browser/android/overscroll_glow.cc b/chromium/content/browser/android/overscroll_glow.cc index 2c9efeea72d..6adf7dbec48 100644 --- a/chromium/content/browser/android/overscroll_glow.cc +++ b/chromium/content/browser/android/overscroll_glow.cc @@ -65,13 +65,14 @@ gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) { } // namespace -scoped_ptr<OverscrollGlow> OverscrollGlow::Create(bool enabled) { +scoped_ptr<OverscrollGlow> OverscrollGlow::Create(bool enabled, + gfx::SizeF size) { const SkBitmap& edge = g_overscroll_resources.Get().edge_bitmap(); const SkBitmap& glow = g_overscroll_resources.Get().glow_bitmap(); if (edge.isNull() || glow.isNull()) return scoped_ptr<OverscrollGlow>(); - return make_scoped_ptr(new OverscrollGlow(enabled, edge, glow)); + return make_scoped_ptr(new OverscrollGlow(enabled, size, edge, glow)); } void OverscrollGlow::EnsureResources() { @@ -79,9 +80,11 @@ void OverscrollGlow::EnsureResources() { } OverscrollGlow::OverscrollGlow(bool enabled, + gfx::SizeF size, const SkBitmap& edge, const SkBitmap& glow) : enabled_(enabled), + size_(size), horizontal_overscroll_enabled_(true), vertical_overscroll_enabled_(true), root_layer_(cc::Layer::Create()) { diff --git a/chromium/content/browser/android/overscroll_glow.h b/chromium/content/browser/android/overscroll_glow.h index 4ea929e9d3e..01156a23f03 100644 --- a/chromium/content/browser/android/overscroll_glow.h +++ b/chromium/content/browser/android/overscroll_glow.h @@ -30,7 +30,7 @@ class OverscrollGlow { // If |enabled| is false, the effect will be be deactivated until // SetEnabled(true) is called. // The caller should attach |root_layer| to the desired layer tree. - static scoped_ptr<OverscrollGlow> Create(bool enabled); + static scoped_ptr<OverscrollGlow> Create(bool enabled, gfx::SizeF size); // Force loading of any necessary resources. This function is thread-safe. static void EnsureResources(); @@ -79,7 +79,10 @@ class OverscrollGlow { private: enum Axis { AXIS_X, AXIS_Y }; - OverscrollGlow(bool enabled, const SkBitmap& edge, const SkBitmap& glow); + OverscrollGlow(bool enabled, + gfx::SizeF size, + const SkBitmap& edge, + const SkBitmap& glow); void Pull(base::TimeTicks current_time, gfx::Vector2dF added_overscroll); diff --git a/chromium/content/browser/android/surface_texture_peer_browser_impl.cc b/chromium/content/browser/android/surface_texture_peer_browser_impl.cc index faaf4a2821d..7935c9ab409 100644 --- a/chromium/content/browser/android/surface_texture_peer_browser_impl.cc +++ b/chromium/content/browser/android/surface_texture_peer_browser_impl.cc @@ -4,11 +4,11 @@ #include "content/browser/android/surface_texture_peer_browser_impl.h" +#include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "media/base/android/media_player_android.h" -#include "media/base/android/media_player_manager.h" #include "ui/gl/android/scoped_java_surface.h" namespace content { @@ -18,7 +18,7 @@ namespace { // Pass a java surface object to the MediaPlayerAndroid object // identified by render process handle, render view ID and player ID. static void SetSurfacePeer( - scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge, + scoped_refptr<gfx::SurfaceTexture> surface_texture, base::ProcessHandle render_process_handle, int render_view_id, int player_id) { @@ -40,7 +40,7 @@ static void SetSurfacePeer( host->media_player_manager()->GetPlayer(player_id); if (player && player != host->media_player_manager()->GetFullscreenPlayer()) { - gfx::ScopedJavaSurface surface(surface_texture_bridge.get()); + gfx::ScopedJavaSurface surface(surface_texture.get()); player->SetVideoSurface(surface.Pass()); } } @@ -57,14 +57,14 @@ SurfaceTexturePeerBrowserImpl::~SurfaceTexturePeerBrowserImpl() { void SurfaceTexturePeerBrowserImpl::EstablishSurfaceTexturePeer( base::ProcessHandle render_process_handle, - scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge, + scoped_refptr<gfx::SurfaceTexture> surface_texture, int render_view_id, int player_id) { - if (!surface_texture_bridge.get()) + if (!surface_texture.get()) return; BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &SetSurfacePeer, surface_texture_bridge, render_process_handle, + &SetSurfacePeer, surface_texture, render_process_handle, render_view_id, player_id)); } diff --git a/chromium/content/browser/android/surface_texture_peer_browser_impl.h b/chromium/content/browser/android/surface_texture_peer_browser_impl.h index 644bf2c92e0..4aa85634dc2 100644 --- a/chromium/content/browser/android/surface_texture_peer_browser_impl.h +++ b/chromium/content/browser/android/surface_texture_peer_browser_impl.h @@ -24,7 +24,7 @@ class SurfaceTexturePeerBrowserImpl : public SurfaceTexturePeer { // SurfaceTexturePeer implementation. virtual void EstablishSurfaceTexturePeer( base::ProcessHandle render_process_handle, - scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge, + scoped_refptr<gfx::SurfaceTexture> surface_texture, int render_view_id, int player_id) OVERRIDE; diff --git a/chromium/content/browser/android/tracing_intent_handler.cc b/chromium/content/browser/android/tracing_intent_handler.cc index 276091619cc..324f8055cc1 100644 --- a/chromium/content/browser/android/tracing_intent_handler.cc +++ b/chromium/content/browser/android/tracing_intent_handler.cc @@ -16,7 +16,7 @@ namespace content { TracingIntentHandler* g_trace_intent_handler = NULL; TracingIntentHandler::TracingIntentHandler(const base::FilePath& path) - : TraceSubscriberStdio(path) { + : TraceSubscriberStdio(path, FILE_TYPE_ARRAY, false) { TraceController::GetInstance()->BeginTracing( this, std::string("-test*"), diff --git a/chromium/content/browser/android/web_contents_observer_android.cc b/chromium/content/browser/android/web_contents_observer_android.cc index eac49b240ae..e30bf953304 100644 --- a/chromium/content/browser/android/web_contents_observer_android.cc +++ b/chromium/content/browser/android/web_contents_observer_android.cc @@ -67,8 +67,8 @@ void WebContentsObserverAndroid::DidStartLoading( ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env)); if (obj.is_null()) return; - ScopedJavaLocalRef<jstring> jstring_url( - ConvertUTF8ToJavaString(env, web_contents()->GetURL().spec())); + ScopedJavaLocalRef<jstring> jstring_url(ConvertUTF8ToJavaString( + env, web_contents()->GetVisibleURL().spec())); Java_WebContentsObserverAndroid_didStartLoading( env, obj.obj(), jstring_url.obj()); } @@ -87,7 +87,7 @@ void WebContentsObserverAndroid::DidStopLoading( if (entry && !entry->GetBaseURLForDataURL().is_empty()) { url_string = entry->GetBaseURLForDataURL().possibly_invalid_spec(); } else { - url_string = web_contents()->GetURL().spec(); + url_string = web_contents()->GetLastCommittedURL().spec(); } ScopedJavaLocalRef<jstring> jstring_url( @@ -212,6 +212,22 @@ void WebContentsObserverAndroid::DidChangeVisibleSSLState() { Java_WebContentsObserverAndroid_didChangeVisibleSSLState(env, obj.obj()); } +void WebContentsObserverAndroid::DidAttachInterstitialPage() { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env)); + if (obj.is_null()) + return; + Java_WebContentsObserverAndroid_didAttachInterstitialPage(env, obj.obj()); +} + +void WebContentsObserverAndroid::DidDetachInterstitialPage() { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env)); + if (obj.is_null()) + return; + Java_WebContentsObserverAndroid_didDetachInterstitialPage(env, obj.obj()); +} + void WebContentsObserverAndroid::DidFailLoadInternal( bool is_provisional_load, bool is_main_frame, diff --git a/chromium/content/browser/android/web_contents_observer_android.h b/chromium/content/browser/android/web_contents_observer_android.h index 35e5526148c..e975d795260 100644 --- a/chromium/content/browser/android/web_contents_observer_android.h +++ b/chromium/content/browser/android/web_contents_observer_android.h @@ -70,6 +70,8 @@ class WebContentsObserverAndroid : public WebContentsObserver { RenderViewHost* render_view_host) OVERRIDE; virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; virtual void DidChangeVisibleSSLState() OVERRIDE; + virtual void DidAttachInterstitialPage() OVERRIDE; + virtual void DidDetachInterstitialPage() OVERRIDE; void DidFailLoadInternal(bool is_provisional_load, bool is_main_frame, diff --git a/chromium/content/browser/aura/browser_compositor_output_surface.cc b/chromium/content/browser/aura/browser_compositor_output_surface.cc index 11fbe0c42a9..36e5dcdbfda 100644 --- a/chromium/content/browser/aura/browser_compositor_output_surface.cc +++ b/chromium/content/browser/aura/browser_compositor_output_surface.cc @@ -11,6 +11,7 @@ #include "base/strings/string_number_conversions.h" #include "cc/output/compositor_frame.h" #include "content/browser/aura/reflector_impl.h" +#include "content/common/gpu/client/context_provider_command_buffer.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" #include "ui/compositor/compositor.h" #include "ui/compositor/compositor_switches.h" @@ -18,12 +19,12 @@ namespace content { BrowserCompositorOutputSurface::BrowserCompositorOutputSurface( - scoped_ptr<WebKit::WebGraphicsContext3D> context, + const scoped_refptr<ContextProviderCommandBuffer>& context_provider, int surface_id, IDMap<BrowserCompositorOutputSurface>* output_surface_map, base::MessageLoopProxy* compositor_message_loop, base::WeakPtr<ui::Compositor> compositor) - : OutputSurface(context.Pass()), + : OutputSurface(context_provider), surface_id_(surface_id), output_surface_map_(output_surface_map), compositor_message_loop_(compositor_message_loop), @@ -70,12 +71,13 @@ void BrowserCompositorOutputSurface::Reshape(gfx::Size size, void BrowserCompositorOutputSurface::SwapBuffers(cc::CompositorFrame* frame) { DCHECK(frame->gl_frame_data); - WebGraphicsContext3DCommandBufferImpl* command_buffer = - static_cast<WebGraphicsContext3DCommandBufferImpl*>(context3d()); + WebGraphicsContext3DCommandBufferImpl* command_buffer_context = + static_cast<WebGraphicsContext3DCommandBufferImpl*>( + context_provider_->Context3d()); CommandBufferProxyImpl* command_buffer_proxy = - command_buffer->GetCommandBufferProxy(); + command_buffer_context->GetCommandBufferProxy(); DCHECK(command_buffer_proxy); - context3d()->shallowFlushCHROMIUM(); + context_provider_->Context3d()->shallowFlushCHROMIUM(); command_buffer_proxy->SetLatencyInfo(frame->metadata.latency_info); if (reflector_.get()) { diff --git a/chromium/content/browser/aura/browser_compositor_output_surface.h b/chromium/content/browser/aura/browser_compositor_output_surface.h index 4b891bdc2ef..f9ec3c8d9d5 100644 --- a/chromium/content/browser/aura/browser_compositor_output_surface.h +++ b/chromium/content/browser/aura/browser_compositor_output_surface.h @@ -15,7 +15,9 @@ namespace base { class MessageLoopProxy; } namespace ui { class Compositor; } namespace content { +class ContextProviderCommandBuffer; class ReflectorImpl; +class WebGraphicsContext3DCommandBufferImpl; // Adapts a WebGraphicsContext3DCommandBufferImpl into a // cc::OutputSurface that also handles vsync parameter updates @@ -25,7 +27,7 @@ class BrowserCompositorOutputSurface public base::NonThreadSafe { public: BrowserCompositorOutputSurface( - scoped_ptr<WebKit::WebGraphicsContext3D> context, + const scoped_refptr<ContextProviderCommandBuffer>& context, int surface_id, IDMap<BrowserCompositorOutputSurface>* output_surface_map, base::MessageLoopProxy* compositor_message_loop, diff --git a/chromium/content/browser/aura/compositor_resize_lock.cc b/chromium/content/browser/aura/compositor_resize_lock.cc new file mode 100644 index 00000000000..e80baba5642 --- /dev/null +++ b/chromium/content/browser/aura/compositor_resize_lock.cc @@ -0,0 +1,65 @@ +// Copyright 2013 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/browser/aura/compositor_resize_lock.h" + +#include "base/debug/trace_event.h" +#include "content/public/browser/browser_thread.h" +#include "ui/aura/root_window.h" +#include "ui/compositor/compositor.h" + +namespace content { + +CompositorResizeLock::CompositorResizeLock(aura::RootWindow* root_window, + const gfx::Size new_size, + bool defer_compositor_lock, + const base::TimeDelta& timeout) + : ResizeLock(new_size, defer_compositor_lock), + root_window_(root_window), + weak_ptr_factory_(this), + cancelled_(false) { + DCHECK(root_window_); + + TRACE_EVENT_ASYNC_BEGIN2("ui", "CompositorResizeLock", this, + "width", expected_size().width(), + "height", expected_size().height()); + root_window_->HoldPointerMoves(); + + BrowserThread::PostDelayedTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&CompositorResizeLock::CancelLock, + weak_ptr_factory_.GetWeakPtr()), + timeout); +} + +CompositorResizeLock::~CompositorResizeLock() { + CancelLock(); + TRACE_EVENT_ASYNC_END2("ui", "CompositorResizeLock", this, + "width", expected_size().width(), + "height", expected_size().height()); +} + +bool CompositorResizeLock::GrabDeferredLock() { + return ResizeLock::GrabDeferredLock(); +} + +void CompositorResizeLock::UnlockCompositor() { + ResizeLock::UnlockCompositor(); + compositor_lock_ = NULL; +} + +void CompositorResizeLock::LockCompositor() { + ResizeLock::LockCompositor(); + compositor_lock_ = root_window_->compositor()->GetCompositorLock(); +} + +void CompositorResizeLock::CancelLock() { + if (cancelled_) + return; + cancelled_ = true; + UnlockCompositor(); + root_window_->ReleasePointerMoves(); +} + +} // namespace content diff --git a/chromium/content/browser/aura/compositor_resize_lock.h b/chromium/content/browser/aura/compositor_resize_lock.h new file mode 100644 index 00000000000..51787e49393 --- /dev/null +++ b/chromium/content/browser/aura/compositor_resize_lock.h @@ -0,0 +1,46 @@ +// Copyright 2013 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_BROWSER_AURA_RESIZE_LOCK_AURA_H_ +#define CONTENT_BROWSER_AURA_RESIZE_LOCK_AURA_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "content/browser/aura/resize_lock.h" + +namespace aura { class RootWindow; } + +namespace ui { class CompositorLock; } + +namespace content { + +// Used to prevent further resizes while a resize is pending. +class CompositorResizeLock : public ResizeLock { + public: + CompositorResizeLock(aura::RootWindow* root_window, + const gfx::Size new_size, + bool defer_compositor_lock, + const base::TimeDelta& timeout); + virtual ~CompositorResizeLock(); + + virtual bool GrabDeferredLock() OVERRIDE; + virtual void UnlockCompositor() OVERRIDE; + + protected: + virtual void LockCompositor() OVERRIDE; + void CancelLock(); + + private: + aura::RootWindow* root_window_; + scoped_refptr<ui::CompositorLock> compositor_lock_; + base::WeakPtrFactory<CompositorResizeLock> weak_ptr_factory_; + bool cancelled_; + + DISALLOW_COPY_AND_ASSIGN(CompositorResizeLock); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_AURA_RESIZE_LOCK_AURA_H_ diff --git a/chromium/content/browser/aura/gpu_process_transport_factory.cc b/chromium/content/browser/aura/gpu_process_transport_factory.cc index 47b1533ae77..959210decea 100644 --- a/chromium/content/browser/aura/gpu_process_transport_factory.cc +++ b/chromium/content/browser/aura/gpu_process_transport_factory.cc @@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/location.h" #include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" #include "cc/output/compositor_frame.h" #include "cc/output/output_surface.h" #include "content/browser/aura/browser_compositor_output_surface.h" @@ -74,6 +75,7 @@ class OwnedTexture : public ui::Texture, ImageTransportFactoryObserver { // ImageTransportFactory overrides: virtual void OnLostResources() OVERRIDE { DeleteTexture(); + host_context_ = NULL; } protected: @@ -90,9 +92,8 @@ class OwnedTexture : public ui::Texture, ImageTransportFactoryObserver { } } - // A raw pointer. This |ImageTransportClientTexture| will be destroyed - // before the |host_context_| via - // |ImageTransportFactoryObserver::OnLostContext()| handlers. + // The OnLostResources() callback will happen before this context + // pointer is destroyed. WebKit::WebGraphicsContext3D* host_context_; unsigned texture_id_; @@ -199,12 +200,6 @@ GpuProcessTransportFactory::CreateOffscreenCommandBufferContext() { return CreateContextCommon(swap_client, 0); } -scoped_ptr<WebKit::WebGraphicsContext3D> -GpuProcessTransportFactory::CreateOffscreenContext() { - return CreateOffscreenCommandBufferContext() - .PassAs<WebKit::WebGraphicsContext3D>(); -} - scoped_ptr<cc::SoftwareOutputDevice> CreateSoftwareOutputDevice( ui::Compositor* compositor) { #if defined(OS_WIN) @@ -225,15 +220,22 @@ scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface( if (!data) data = CreatePerCompositorData(compositor); - scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context; + scoped_refptr<ContextProviderCommandBuffer> context_provider; + CommandLine* command_line = CommandLine::ForCurrentProcess(); if (!command_line->HasSwitch(switches::kUIEnableSoftwareCompositing)) { - context = - CreateContextCommon(data->swap_client->AsWeakPtr(), data->surface_id); + context_provider = ContextProviderCommandBuffer::Create( + GpuProcessTransportFactory::CreateContextCommon( + data->swap_client->AsWeakPtr(), + data->surface_id), + "Compositor"); } - if (!context) { + + UMA_HISTOGRAM_BOOLEAN("Aura.CreatedGpuBrowserCompositor", !!context_provider); + + if (!context_provider.get()) { if (ui::Compositor::WasInitializedWithThread()) { - LOG(FATAL) << "Failed to create UI context, but can't use software " + LOG(FATAL) << "Failed to create UI context, but can't use software" " compositing with browser threaded compositing. Aborting."; } @@ -255,7 +257,7 @@ scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface( scoped_ptr<BrowserCompositorOutputSurface> surface( new BrowserCompositorOutputSurface( - context.PassAs<WebKit::WebGraphicsContext3D>(), + context_provider, per_compositor_data_[compositor]->surface_id, &output_surface_map_, base::MessageLoopProxy::current().get(), @@ -390,12 +392,14 @@ void GpuProcessTransportFactory::RemoveObserver( scoped_refptr<cc::ContextProvider> GpuProcessTransportFactory::OffscreenContextProviderForMainThread() { - if (!shared_contexts_main_thread_.get() || - shared_contexts_main_thread_->DestroyedOnMainThread()) { + // Don't check for DestroyedOnMainThread() here. We hear about context + // loss for this context through the lost context callback. If the context + // is lost, we want to leave this ContextProvider available until the lost + // context notification is sent to the ImageTransportFactoryObserver clients. + if (!shared_contexts_main_thread_.get()) { shared_contexts_main_thread_ = ContextProviderCommandBuffer::Create( - base::Bind(&GpuProcessTransportFactory:: - CreateOffscreenCommandBufferContext, - base::Unretained(this))); + GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(), + "Compositor-Offscreen-MainThread"); if (shared_contexts_main_thread_) { shared_contexts_main_thread_->SetLostContextCallback(base::Bind( &GpuProcessTransportFactory:: @@ -411,12 +415,13 @@ GpuProcessTransportFactory::OffscreenContextProviderForMainThread() { scoped_refptr<cc::ContextProvider> GpuProcessTransportFactory::OffscreenContextProviderForCompositorThread() { - if (!shared_contexts_compositor_thread_.get() || - shared_contexts_compositor_thread_->DestroyedOnMainThread()) { + // The lifetime of this context is tied to the main thread context so that + // they are always in the same share group. So do not check for + // DestroyedOnMainThread(). + if (!shared_contexts_compositor_thread_.get()) { shared_contexts_compositor_thread_ = ContextProviderCommandBuffer::Create( - base::Bind(&GpuProcessTransportFactory:: - CreateOffscreenCommandBufferContext, - base::Unretained(this))); + GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(), + "Compositor-Offscreen"); } return shared_contexts_compositor_thread_; } @@ -500,18 +505,29 @@ void GpuProcessTransportFactory::OnLostMainThreadSharedContextInsideCallback() { void GpuProcessTransportFactory::OnLostMainThreadSharedContext() { LOG(ERROR) << "Lost UI shared context."; + // Keep old resources around while we call the observers, but ensure that // new resources are created if needed. + // Kill shared contexts for both threads in tandem so they are always in + // the same share group. - scoped_refptr<ContextProviderCommandBuffer> old_contexts_main_thread = + scoped_refptr<cc::ContextProvider> lost_shared_contexts_main_thread = shared_contexts_main_thread_; + scoped_refptr<cc::ContextProvider> lost_shared_contexts_compositor_thread = + shared_contexts_compositor_thread_; shared_contexts_main_thread_ = NULL; + shared_contexts_compositor_thread_ = NULL; - scoped_ptr<GLHelper> old_helper(gl_helper_.release()); + scoped_ptr<GLHelper> lost_gl_helper = gl_helper_.Pass(); FOR_EACH_OBSERVER(ImageTransportFactoryObserver, observer_list_, OnLostResources()); + + // Kill things that use the shared context before killing the shared context. + lost_gl_helper.reset(); + lost_shared_contexts_main_thread = NULL; + lost_shared_contexts_compositor_thread = NULL; } } // namespace content diff --git a/chromium/content/browser/aura/gpu_process_transport_factory.h b/chromium/content/browser/aura/gpu_process_transport_factory.h index 72b88d532bb..56d3e55498a 100644 --- a/chromium/content/browser/aura/gpu_process_transport_factory.h +++ b/chromium/content/browser/aura/gpu_process_transport_factory.h @@ -35,9 +35,7 @@ class GpuProcessTransportFactory scoped_ptr<WebGraphicsContext3DCommandBufferImpl> CreateOffscreenCommandBufferContext(); - // ContextFactory implementation. - virtual scoped_ptr<WebKit::WebGraphicsContext3D> CreateOffscreenContext() - OVERRIDE; + // ui::ContextFactory implementation. virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface( ui::Compositor* compositor) OVERRIDE; virtual scoped_refptr<ui::Reflector> CreateReflector( @@ -46,6 +44,10 @@ class GpuProcessTransportFactory virtual void RemoveReflector( scoped_refptr<ui::Reflector> reflector) OVERRIDE; virtual void RemoveCompositor(ui::Compositor* compositor) OVERRIDE; + virtual scoped_refptr<cc::ContextProvider> + OffscreenContextProviderForMainThread() OVERRIDE; + virtual scoped_refptr<cc::ContextProvider> + OffscreenContextProviderForCompositorThread() OVERRIDE; virtual bool DoesCreateTestContexts() OVERRIDE; // ImageTransportFactory implementation. @@ -66,12 +68,6 @@ class GpuProcessTransportFactory virtual void RemoveObserver( ImageTransportFactoryObserver* observer) OVERRIDE; - // ui::ContextFactory implementation. - virtual scoped_refptr<cc::ContextProvider> - OffscreenContextProviderForMainThread() OVERRIDE; - virtual scoped_refptr<cc::ContextProvider> - OffscreenContextProviderForCompositorThread() OVERRIDE; - void OnLostContext(ui::Compositor* compositor); private: diff --git a/chromium/content/browser/aura/image_transport_factory.cc b/chromium/content/browser/aura/image_transport_factory.cc index 28b04bf7ea4..e38ae880bbb 100644 --- a/chromium/content/browser/aura/image_transport_factory.cc +++ b/chromium/content/browser/aura/image_transport_factory.cc @@ -44,6 +44,7 @@ static bool UseTestContextAndTransportFactory() { // static void ImageTransportFactory::Initialize() { + DCHECK(!g_factory); if (UseTestContextAndTransportFactory()) { g_factory = new NoTransportImageTransportFactory(new ui::TestContextFactory); @@ -53,6 +54,12 @@ void ImageTransportFactory::Initialize() { ui::ContextFactory::SetInstance(g_factory->AsContextFactory()); } +void ImageTransportFactory::InitializeForUnitTests() { + DCHECK(!g_factory); + g_factory = new NoTransportImageTransportFactory(new ui::TestContextFactory); + ui::ContextFactory::SetInstance(g_factory->AsContextFactory()); +} + // static void ImageTransportFactory::Terminate() { ui::ContextFactory::SetInstance(NULL); diff --git a/chromium/content/browser/aura/image_transport_factory.h b/chromium/content/browser/aura/image_transport_factory.h index 85fba7d5300..7cd2523c421 100644 --- a/chromium/content/browser/aura/image_transport_factory.h +++ b/chromium/content/browser/aura/image_transport_factory.h @@ -8,6 +8,7 @@ #include <string> #include "base/memory/ref_counted.h" +#include "content/common/content_export.h" #include "ui/gfx/native_widget_types.h" namespace gfx { @@ -27,7 +28,7 @@ namespace content { class GLHelper; // This class provides a way to get notified when surface handles get lost. -class ImageTransportFactoryObserver { +class CONTENT_EXPORT ImageTransportFactoryObserver { public: virtual ~ImageTransportFactoryObserver() {} @@ -44,13 +45,17 @@ class ImageTransportFactoryObserver { // cross-process image transport, both for creating the shared surface handle // (destination surface for the GPU process) and the transport client (logic for // using that surface as a texture). The factory is a process-wide singleton. -class ImageTransportFactory { +class CONTENT_EXPORT ImageTransportFactory { public: virtual ~ImageTransportFactory() {} - // Initialize the global transport factory. + // Initializes the global transport factory. static void Initialize(); + // Initializes the global transport factory for unit tests, using a test + // context. + static void InitializeForUnitTests(); + // Terminates the global transport factory. static void Terminate(); diff --git a/chromium/content/browser/aura/no_transport_image_transport_factory.cc b/chromium/content/browser/aura/no_transport_image_transport_factory.cc index 18e8dad9072..6fa80c69095 100644 --- a/chromium/content/browser/aura/no_transport_image_transport_factory.cc +++ b/chromium/content/browser/aura/no_transport_image_transport_factory.cc @@ -3,13 +3,51 @@ // found in the LICENSE file. #include "content/browser/aura/no_transport_image_transport_factory.h" + +#include "cc/output/context_provider.h" +#include "content/common/gpu/client/gl_helper.h" +#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "ui/compositor/compositor.h" namespace content { +namespace { + +class FakeTexture : public ui::Texture { + public: + FakeTexture(scoped_refptr<cc::ContextProvider> context_provider, + float device_scale_factor) + : ui::Texture(false, gfx::Size(), device_scale_factor), + context_provider_(context_provider), + texture_(context_provider_->Context3d()->createTexture()) {} + + virtual unsigned int PrepareTexture() OVERRIDE { return texture_; } + virtual WebKit::WebGraphicsContext3D* HostContext3D() OVERRIDE { + return context_provider_->Context3d(); + } + + virtual void Consume(const std::string& mailbox_name, + const gfx::Size& new_size) OVERRIDE { + size_ = new_size; + } + + private: + virtual ~FakeTexture() { + context_provider_->Context3d()->deleteTexture(texture_); + } + + scoped_refptr<cc::ContextProvider> context_provider_; + unsigned texture_; + DISALLOW_COPY_AND_ASSIGN(FakeTexture); +}; + +} // anonymous namespace + NoTransportImageTransportFactory::NoTransportImageTransportFactory( ui::ContextFactory* context_factory) - : context_factory_(context_factory) {} + : context_factory_(context_factory), + context_provider_( + context_factory_->OffscreenContextProviderForMainThread()) {} NoTransportImageTransportFactory::~NoTransportImageTransportFactory() {} @@ -28,7 +66,7 @@ void NoTransportImageTransportFactory::DestroySharedSurfaceHandle( scoped_refptr<ui::Texture> NoTransportImageTransportFactory::CreateTransportClient( float device_scale_factor) { - return NULL; + return new FakeTexture(context_provider_, device_scale_factor); } scoped_refptr<ui::Texture> NoTransportImageTransportFactory::CreateOwnedTexture( @@ -38,7 +76,11 @@ scoped_refptr<ui::Texture> NoTransportImageTransportFactory::CreateOwnedTexture( return NULL; } -GLHelper* NoTransportImageTransportFactory::GetGLHelper() { return NULL; } +GLHelper* NoTransportImageTransportFactory::GetGLHelper() { + if (!gl_helper_) + gl_helper_.reset(new GLHelper(context_provider_->Context3d())); + return gl_helper_.get(); +} uint32 NoTransportImageTransportFactory::InsertSyncPoint() { return 0; } diff --git a/chromium/content/browser/aura/no_transport_image_transport_factory.h b/chromium/content/browser/aura/no_transport_image_transport_factory.h index 8254faf5fa5..0d571643afd 100644 --- a/chromium/content/browser/aura/no_transport_image_transport_factory.h +++ b/chromium/content/browser/aura/no_transport_image_transport_factory.h @@ -8,6 +8,10 @@ #include "base/memory/scoped_ptr.h" #include "content/browser/aura/image_transport_factory.h" +namespace cc { +class ContextProvider; +} + namespace content { // An ImageTransportFactory that disables transport. @@ -36,6 +40,8 @@ class NoTransportImageTransportFactory : public ImageTransportFactory { private: scoped_ptr<ui::ContextFactory> context_factory_; + scoped_refptr<cc::ContextProvider> context_provider_; + scoped_ptr<GLHelper> gl_helper_; DISALLOW_COPY_AND_ASSIGN(NoTransportImageTransportFactory); }; diff --git a/chromium/content/browser/aura/reflector_impl.cc b/chromium/content/browser/aura/reflector_impl.cc index 1e26cf8d18e..78726ecdcc2 100644 --- a/chromium/content/browser/aura/reflector_impl.cc +++ b/chromium/content/browser/aura/reflector_impl.cc @@ -68,7 +68,8 @@ void ReflectorImpl::ShutdownOnImplThread() { // ImplThread. void ReflectorImpl::AttachToOutputSurface( BrowserCompositorOutputSurface* output_surface) { - gl_helper_.reset(new GLHelper(output_surface->context3d())); + gl_helper_.reset( + new GLHelper(output_surface->context_provider()->Context3d())); output_surface->SetReflector(this); } diff --git a/chromium/content/browser/aura/resize_lock.cc b/chromium/content/browser/aura/resize_lock.cc new file mode 100644 index 00000000000..4c8cd95a67f --- /dev/null +++ b/chromium/content/browser/aura/resize_lock.cc @@ -0,0 +1,35 @@ +// Copyright 2013 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/browser/aura/resize_lock.h" + +namespace content { + +ResizeLock::ResizeLock(const gfx::Size new_size, bool defer_compositor_lock) + : new_size_(new_size), + defer_compositor_lock_(defer_compositor_lock) { + if (!defer_compositor_lock_) + LockCompositor(); +} + +ResizeLock::~ResizeLock() { + UnlockCompositor(); +} + +bool ResizeLock::GrabDeferredLock() { + if (!defer_compositor_lock_) + return false; + LockCompositor(); + return true; +} + +void ResizeLock::UnlockCompositor() { + defer_compositor_lock_ = false; +} + +void ResizeLock::LockCompositor() { + defer_compositor_lock_ = false; +} + +} // namespace content diff --git a/chromium/content/browser/aura/resize_lock.h b/chromium/content/browser/aura/resize_lock.h new file mode 100644 index 00000000000..cff98224c7f --- /dev/null +++ b/chromium/content/browser/aura/resize_lock.h @@ -0,0 +1,37 @@ +// Copyright 2013 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_BROWSER_AURA_RESIZE_LOCK_H_ +#define CONTENT_BROWSER_AURA_RESIZE_LOCK_H_ + +#include "base/basictypes.h" +#include "content/common/content_export.h" +#include "ui/gfx/size.h" + +namespace content { + +class CONTENT_EXPORT ResizeLock { + public: + virtual ~ResizeLock(); + + virtual bool GrabDeferredLock(); + virtual void UnlockCompositor(); + + const gfx::Size& expected_size() const { return new_size_; } + + protected: + ResizeLock(const gfx::Size new_size, bool defer_compositor_lock); + + virtual void LockCompositor(); + + private: + gfx::Size new_size_; + bool defer_compositor_lock_; + + DISALLOW_COPY_AND_ASSIGN(ResizeLock); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_AURA_RESIZE_LOCK_H_ diff --git a/chromium/content/browser/aura/software_browser_compositor_output_surface.cc b/chromium/content/browser/aura/software_browser_compositor_output_surface.cc index c59806e83f9..1f92582841e 100644 --- a/chromium/content/browser/aura/software_browser_compositor_output_surface.cc +++ b/chromium/content/browser/aura/software_browser_compositor_output_surface.cc @@ -8,7 +8,7 @@ #include "cc/output/compositor_frame.h" #include "cc/output/software_output_device.h" #include "content/browser/renderer_host/render_widget_host_impl.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" namespace content { diff --git a/chromium/content/browser/aura/software_output_device_win.cc b/chromium/content/browser/aura/software_output_device_win.cc index 0332e3e936d..7937810e2ba 100644 --- a/chromium/content/browser/aura/software_output_device_win.cc +++ b/chromium/content/browser/aura/software_output_device_win.cc @@ -11,6 +11,7 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/canvas_skia_paint.h" #include "ui/gfx/gdi_util.h" +#include "ui/gfx/skia_util.h" namespace content { @@ -35,7 +36,7 @@ void SoftwareOutputDeviceWin::Resize(gfx::Size viewport_size) { return; viewport_size_ = viewport_size; - contents_.reset(new gfx::Canvas(viewport_size, ui::SCALE_FACTOR_100P, false)); + contents_.reset(new gfx::Canvas(viewport_size, ui::SCALE_FACTOR_100P, true)); memset(&bitmap_info_, 0, sizeof(bitmap_info_)); gfx::CreateBitmapHeader(viewport_size_.width(), viewport_size_.height(), &bitmap_info_.bmiHeader); @@ -84,18 +85,19 @@ void SoftwareOutputDeviceWin::EndPaint(cc::SoftwareFrameData* frame_data) { RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); skia::EndPlatformPaint(canvas); } else { - SkDevice* device = canvas->getDevice(); - const SkBitmap& bitmap = device->accessBitmap(false); HDC hdc = ::GetDC(hwnd_); - gfx::StretchDIBits(hdc, - rect.x(), rect.y(), - rect.width(), rect.height(), - rect.x(), rect.y(), - rect.width(), rect.height(), - bitmap.getPixels(), - &bitmap_info_); + RECT src_rect = rect.ToRECT(); + skia::DrawToNativeContext(canvas, hdc, rect.x(), rect.y(), &src_rect); ::ReleaseDC(hwnd_, hdc); } } +void SoftwareOutputDeviceWin::CopyToBitmap( + gfx::Rect rect, SkBitmap* output) { + DCHECK(contents_); + SkBaseDevice* device = contents_->sk_canvas()->getDevice(); + const SkBitmap& bitmap = device->accessBitmap(false); + bitmap.extractSubset(output, gfx::RectToSkIRect(rect)); +} + } // namespace content diff --git a/chromium/content/browser/aura/software_output_device_win.h b/chromium/content/browser/aura/software_output_device_win.h index 93568c2bd5b..9c0655b6f67 100644 --- a/chromium/content/browser/aura/software_output_device_win.h +++ b/chromium/content/browser/aura/software_output_device_win.h @@ -28,6 +28,7 @@ class SoftwareOutputDeviceWin : public cc::SoftwareOutputDevice { virtual void Resize(gfx::Size viewport_size) OVERRIDE; virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE; virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE; + virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE; private: HWND hwnd_; diff --git a/chromium/content/browser/aura/software_output_device_x11.cc b/chromium/content/browser/aura/software_output_device_x11.cc index 229755b6ffb..abdb116ed34 100644 --- a/chromium/content/browser/aura/software_output_device_x11.cc +++ b/chromium/content/browser/aura/software_output_device_x11.cc @@ -16,7 +16,7 @@ namespace content { SoftwareOutputDeviceX11::SoftwareOutputDeviceX11(ui::Compositor* compositor) : compositor_(compositor), - display_(ui::GetXDisplay()), + display_(gfx::GetXDisplay()), gc_(NULL), image_(NULL) { // TODO(skaslev) Remove this when crbug.com/180702 is fixed. diff --git a/chromium/content/browser/aura/software_output_device_x11.h b/chromium/content/browser/aura/software_output_device_x11.h index 986a486d754..782afeee7ec 100644 --- a/chromium/content/browser/aura/software_output_device_x11.h +++ b/chromium/content/browser/aura/software_output_device_x11.h @@ -6,7 +6,7 @@ #define CONTENT_BROWSER_AURA_SOFTWARE_OUTPUT_DEVICE_X11_H_ #include "cc/output/software_output_device.h" -#include "ui/base/x/x11_util.h" +#include "ui/gfx/x/x11_types.h" namespace ui { class Compositor; @@ -28,7 +28,7 @@ class SoftwareOutputDeviceX11 : public cc::SoftwareOutputDevice { void ClearImage(); ui::Compositor* compositor_; - Display* display_; + XDisplay* display_; GC gc_; XImage* image_; }; diff --git a/chromium/content/browser/bookmarklet_browsertest.cc b/chromium/content/browser/bookmarklet_browsertest.cc index 02f419302a2..35523ee63dc 100644 --- a/chromium/content/browser/bookmarklet_browsertest.cc +++ b/chromium/content/browser/bookmarklet_browsertest.cc @@ -6,7 +6,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/content/browser/browser_child_process_host_impl.cc b/chromium/content/browser/browser_child_process_host_impl.cc index 780dc6a2d42..5cc953281d3 100644 --- a/chromium/content/browser/browser_child_process_host_impl.cc +++ b/chromium/content/browser/browser_child_process_host_impl.cc @@ -133,7 +133,7 @@ void BrowserChildProcessHostImpl::Launch( SandboxedProcessLauncherDelegate* delegate, #elif defined(OS_POSIX) bool use_zygote, - const base::EnvironmentVector& environ, + const base::EnvironmentMap& environ, #endif CommandLine* cmd_line) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -153,6 +153,9 @@ void BrowserChildProcessHostImpl::Launch( #if defined(OS_POSIX) switches::kChildCleanExit, #endif +#if defined(OS_WIN) + switches::kEnableHighResolutionTime, +#endif }; cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches, arraysize(kForwardSwitches)); @@ -223,11 +226,11 @@ void BrowserChildProcessHostImpl::NotifyProcessInstanceCreated( } base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus( - int* exit_code) { + bool known_dead, int* exit_code) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!child_process_) // If the delegate doesn't use Launch() helper. return base::GetTerminationStatus(data_.handle, exit_code); - return child_process_->GetChildTerminationStatus(false /* known_dead */, + return child_process_->GetChildTerminationStatus(known_dead, exit_code); } @@ -264,7 +267,8 @@ void BrowserChildProcessHostImpl::OnChildDisconnected() { if (child_process_.get() || data_.handle) { DCHECK(data_.handle != base::kNullProcessHandle); int exit_code; - base::TerminationStatus status = GetTerminationStatus(&exit_code); + base::TerminationStatus status = GetTerminationStatus( + true /* known_dead */, &exit_code); switch (status) { case base::TERMINATION_STATUS_PROCESS_CRASHED: case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: { diff --git a/chromium/content/browser/browser_child_process_host_impl.h b/chromium/content/browser/browser_child_process_host_impl.h index 61971d6d978..7540209f5cc 100644 --- a/chromium/content/browser/browser_child_process_host_impl.h +++ b/chromium/content/browser/browser_child_process_host_impl.h @@ -46,12 +46,13 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl SandboxedProcessLauncherDelegate* delegate, #elif defined(OS_POSIX) bool use_zygote, - const base::EnvironmentVector& environ, + const base::EnvironmentMap& environ, #endif CommandLine* cmd_line) OVERRIDE; virtual const ChildProcessData& GetData() const OVERRIDE; virtual ChildProcessHost* GetHost() const OVERRIDE; - virtual base::TerminationStatus GetTerminationStatus(int* exit_code) OVERRIDE; + virtual base::TerminationStatus GetTerminationStatus( + bool known_dead, int* exit_code) OVERRIDE; virtual void SetName(const string16& name) OVERRIDE; virtual void SetHandle(base::ProcessHandle handle) OVERRIDE; diff --git a/chromium/content/browser/browser_main_loop.cc b/chromium/content/browser/browser_main_loop.cc index c51b5c59122..14dd61410bc 100644 --- a/chromium/content/browser/browser_main_loop.cc +++ b/chromium/content/browser/browser_main_loop.cc @@ -20,13 +20,15 @@ #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/system_monitor/system_monitor.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread_restrictions.h" #include "base/timer/hi_res_timer_manager.h" #include "content/browser/browser_thread_impl.h" -#include "content/browser/device_orientation/device_motion_service.h" +#include "content/browser/device_orientation/device_inertial_sensor_service.h" #include "content/browser/download/save_file_manager.h" #include "content/browser/gamepad/gamepad_service.h" #include "content/browser/gpu/browser_gpu_channel_host_factory.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/gpu/gpu_process_host_ui_shim.h" @@ -43,7 +45,6 @@ #include "content/browser/webui/url_data_manager.h" #include "content/public/browser/browser_main_parts.h" #include "content/public/browser/browser_shutdown.h" -#include "content/public/browser/compositor_util.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/content_switches.h" @@ -52,6 +53,7 @@ #include "crypto/nss_util.h" #include "media/audio/audio_manager.h" #include "media/base/media.h" +#include "media/base/user_input_monitor.h" #include "media/midi/midi_manager.h" #include "net/base/network_change_notifier.h" #include "net/socket/client_socket_factory.h" @@ -64,7 +66,7 @@ #if defined(OS_ANDROID) #include "base/android/jni_android.h" -#include "content/browser/android/browser_startup_config.h" +#include "content/browser/android/browser_startup_controller.h" #include "content/browser/android/surface_texture_peer_browser_impl.h" #endif @@ -80,7 +82,7 @@ #include "ui/base/l10n/l10n_util_win.h" #endif -#if defined(OS_LINUX) || defined(OS_OPENBSD) +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) #include <glib-object.h> #endif @@ -173,7 +175,7 @@ void SetupSandbox(const CommandLine& parsed_command_line) { } #endif -#if defined(OS_LINUX) || defined(OS_OPENBSD) +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) static void GLibLogHandler(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, @@ -419,6 +421,7 @@ void BrowserMainLoop::MainMessageLoopStart() { network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); } +#if !defined(OS_IOS) { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MediaFeatures") media::InitializeCPUSpecificMediaFeatures(); @@ -431,8 +434,6 @@ void BrowserMainLoop::MainMessageLoopStart() { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MIDIManager") midi_manager_.reset(media::MIDIManager::Create()); } - -#if !defined(OS_IOS) { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:ContentWebUIController") WebUIControllerFactory::RegisterFactory( @@ -455,6 +456,11 @@ void BrowserMainLoop::MainMessageLoopStart() { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:OnlineStateObserver") online_state_observer_.reset(new BrowserOnlineStateObserver); } + + { + system_stats_monitor_.reset(new base::debug::TraceEventSystemStatsMonitor( + base::ThreadTaskRunnerHandle::Get())); + } #endif // !defined(OS_IOS) #if defined(OS_WIN) @@ -526,36 +532,53 @@ int BrowserMainLoop::PreCreateThreads() { } void BrowserMainLoop::CreateStartupTasks() { - TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks") + TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks"); + // First time through, we really want to create all the tasks + if (!startup_task_runner_.get()) { #if defined(OS_ANDROID) - scoped_refptr<StartupTaskRunner> task_runner = - new StartupTaskRunner(BrowserMayStartAsynchronously(), - base::Bind(&BrowserStartupComplete), - base::MessageLoop::current()->message_loop_proxy()); + startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner( + base::Bind(&BrowserStartupComplete), + base::MessageLoop::current()->message_loop_proxy())); #else - scoped_refptr<StartupTaskRunner> task_runner = - new StartupTaskRunner(false, - base::Callback<void(int)>(), - base::MessageLoop::current()->message_loop_proxy()); + startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner( + base::Callback<void(int)>(), + base::MessageLoop::current()->message_loop_proxy())); #endif - StartupTask pre_create_threads = - base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this)); - task_runner->AddTask(pre_create_threads); + StartupTask pre_create_threads = + base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this)); + startup_task_runner_->AddTask(pre_create_threads); - StartupTask create_threads = - base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this)); - task_runner->AddTask(create_threads); + StartupTask create_threads = + base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this)); + startup_task_runner_->AddTask(create_threads); - StartupTask browser_thread_started = base::Bind( - &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this)); - task_runner->AddTask(browser_thread_started); + StartupTask browser_thread_started = base::Bind( + &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this)); + startup_task_runner_->AddTask(browser_thread_started); - StartupTask pre_main_message_loop_run = base::Bind( - &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this)); - task_runner->AddTask(pre_main_message_loop_run); + StartupTask pre_main_message_loop_run = base::Bind( + &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this)); + startup_task_runner_->AddTask(pre_main_message_loop_run); - task_runner->StartRunningTasks(); +#if defined(OS_ANDROID) + if (BrowserMayStartAsynchronously()) { + startup_task_runner_->StartRunningTasksAsync(); + } +#endif + } +#if defined(OS_ANDROID) + if (!BrowserMayStartAsynchronously()) { + // A second request for asynchronous startup can be ignored, so + // StartupRunningTasksAsync is only called first time through. If, however, + // this is a request for synchronous startup then it must override any + // previous call for async startup, so we call RunAllTasksNow() + // unconditionally. + startup_task_runner_->RunAllTasksNow(); + } +#else + startup_task_runner_->RunAllTasksNow(); +#endif } int BrowserMainLoop::CreateThreads() { @@ -681,6 +704,8 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { // Called early, nothing to do return; } + TRACE_EVENT0("shutdown", "BrowserMainLoop::ShutdownThreadsAndCleanUp") + // Teardown may start in PostMainMessageLoopRun, and during teardown we // need to be able to perform IO. base::ThreadRestrictions::SetIOAllowed(true); @@ -689,24 +714,37 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { base::Bind(base::IgnoreResult(&base::ThreadRestrictions::SetIOAllowed), true)); - if (parts_) + if (parts_) { + TRACE_EVENT0("shutdown", + "BrowserMainLoop::Subsystem:PostMainMessageLoopRun"); parts_->PostMainMessageLoopRun(); + } trace_memory_controller_.reset(); + system_stats_monitor_.reset(); #if !defined(OS_IOS) // Destroying the GpuProcessHostUIShims on the UI thread posts a task to // delete related objects on the GPU thread. This must be done before // stopping the GPU thread. The GPU thread will close IPC channels to renderer // processes so this has to happen before stopping the IO thread. - GpuProcessHostUIShim::DestroyAll(); - + { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:GPUProcessHostShim"); + GpuProcessHostUIShim::DestroyAll(); + } // Cancel pending requests and prevent new requests. - if (resource_dispatcher_host_) + if (resource_dispatcher_host_) { + TRACE_EVENT0("shutdown", + "BrowserMainLoop::Subsystem:ResourceDispatcherHost"); resource_dispatcher_host_.get()->Shutdown(); + } #if defined(USE_AURA) - ImageTransportFactory::Terminate(); + { + TRACE_EVENT0("shutdown", + "BrowserMainLoop::Subsystem:ImageTransportFactory"); + ImageTransportFactory::Terminate(); + } #endif // The device monitors are using |system_monitor_| as dependency, so delete @@ -743,29 +781,42 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { // // - (Not sure why DB stops last.) switch (thread_id) { - case BrowserThread::DB: - db_thread_.reset(); + case BrowserThread::DB: { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DBThread"); + db_thread_.reset(); + } break; - case BrowserThread::FILE_USER_BLOCKING: - file_user_blocking_thread_.reset(); + case BrowserThread::FILE_USER_BLOCKING: { + TRACE_EVENT0("shutdown", + "BrowserMainLoop::Subsystem:FileUserBlockingThread"); + file_user_blocking_thread_.reset(); + } break; - case BrowserThread::FILE: + case BrowserThread::FILE: { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread"); #if !defined(OS_IOS) - // Clean up state that lives on or uses the file_thread_ before - // it goes away. - if (resource_dispatcher_host_) - resource_dispatcher_host_.get()->save_file_manager()->Shutdown(); + // Clean up state that lives on or uses the file_thread_ before + // it goes away. + if (resource_dispatcher_host_) + resource_dispatcher_host_.get()->save_file_manager()->Shutdown(); #endif // !defined(OS_IOS) - file_thread_.reset(); + file_thread_.reset(); + } break; - case BrowserThread::PROCESS_LAUNCHER: - process_launcher_thread_.reset(); + case BrowserThread::PROCESS_LAUNCHER: { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:LauncherThread"); + process_launcher_thread_.reset(); + } break; - case BrowserThread::CACHE: - cache_thread_.reset(); + case BrowserThread::CACHE: { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:CacheThread"); + cache_thread_.reset(); + } break; - case BrowserThread::IO: - io_thread_.reset(); + case BrowserThread::IO: { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:IOThread"); + io_thread_.reset(); + } break; case BrowserThread::UI: case BrowserThread::ID_COUNT: @@ -776,7 +827,10 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { } #if !defined(OS_IOS) - indexed_db_thread_.reset(); + { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:IndexedDBThread"); + indexed_db_thread_.reset(); + } #endif // Close the blocking I/O pool after the other threads. Other threads such @@ -785,23 +839,39 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { // may also be slow operations pending that will blcok shutdown, so closing // it here (which will block until required operations are complete) gives // more head start for those operations to finish. - BrowserThreadImpl::ShutdownThreadPool(); + { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:ThreadPool"); + BrowserThreadImpl::ShutdownThreadPool(); + } #if !defined(OS_IOS) // Must happen after the IO thread is shutdown since this may be accessed from // it. - BrowserGpuChannelHostFactory::Terminate(); + { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:GPUChannelFactory"); + BrowserGpuChannelHostFactory::Terminate(); + } // Must happen after the I/O thread is shutdown since this class lives on the // I/O thread and isn't threadsafe. - GamepadService::GetInstance()->Terminate(); - DeviceMotionService::GetInstance()->Shutdown(); - - URLDataManager::DeleteDataSources(); + { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:GamepadService"); + GamepadService::GetInstance()->Terminate(); + } + { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:SensorService"); + DeviceInertialSensorService::GetInstance()->Shutdown(); + } + { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DeleteDataSources"); + URLDataManager::DeleteDataSources(); + } #endif // !defined(OS_IOS) - if (parts_) + if (parts_) { + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:PostDestroyThreads"); parts_->PostDestroyThreads(); + } } void BrowserMainLoop::InitializeMainThread() { @@ -872,6 +942,14 @@ int BrowserMainLoop::BrowserThreadsStarted() { audio_manager_.get(), media_stream_manager_.get())); } + { + TRACE_EVENT0( + "startup", + "BrowserMainLoop::BrowserThreadsStarted::InitUserInputMonitor"); + user_input_monitor_ = media::UserInputMonitor::Create( + io_thread_->message_loop_proxy(), main_thread_->message_loop_proxy()); + } + // Alert the clipboard class to which threads are allowed to access the // clipboard: std::vector<base::PlatformThreadId> allowed_clipboard_threads; @@ -887,8 +965,13 @@ int BrowserMainLoop::BrowserThreadsStarted() { // When running the GPU thread in-process, avoid optimistically starting it // since creating the GPU thread races against creation of the one-and-only // ChildProcess instance which is created by the renderer thread. + bool always_uses_gpu = IsForceCompositingModeEnabled(); +#if defined(USE_AURA) + if (!GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) + always_uses_gpu = false; +#endif if (GpuDataManagerImpl::GetInstance()->GpuAccessAllowed(NULL) && - IsForceCompositingModeEnabled() && + always_uses_gpu && !parsed_command_line_.HasSwitch(switches::kDisableGpuProcessPrelaunch) && !parsed_command_line_.HasSwitch(switches::kSingleProcess) && !parsed_command_line_.HasSwitch(switches::kInProcessGPU)) { @@ -911,7 +994,7 @@ void BrowserMainLoop::InitializeToolkit() { // are no #else branches on any #ifs. // TODO(stevenjb): Move platform specific code into platform specific Parts // (Need to add InitializeToolkit stage to BrowserParts). -#if defined(OS_LINUX) || defined(OS_OPENBSD) +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) // g_type_init will be deprecated in 2.36. 2.35 is the development // version for 2.36, hence do not call g_type_init starting 2.35. // http://developer.gnome.org/gobject/unstable/gobject-Type-Information.html#g-type-init diff --git a/chromium/content/browser/browser_main_loop.h b/chromium/content/browser/browser_main_loop.h index 7b2879f70e4..821da872755 100644 --- a/chromium/content/browser/browser_main_loop.h +++ b/chromium/content/browser/browser_main_loop.h @@ -20,12 +20,14 @@ class PowerMonitor; class SystemMonitor; namespace debug { class TraceMemoryController; +class TraceEventSystemStatsMonitor; } // namespace debug } // namespace base namespace media { class AudioManager; class MIDIManager; +class UserInputMonitor; } // namespace media namespace net { @@ -41,6 +43,7 @@ class BrowserThreadImpl; class MediaStreamManager; class ResourceDispatcherHostImpl; class SpeechRecognitionManagerImpl; +class StartupTaskRunner; class SystemMessageWindowWin; struct MainFunctionParams; @@ -67,7 +70,10 @@ class CONTENT_EXPORT BrowserMainLoop { void InitializeToolkit(); void MainMessageLoopStart(); - // Create the tasks we need to complete startup. + // Create and start running the tasks we need to complete startup. Note that + // this can be called more than once (currently only on Android) if we get a + // request for synchronous startup while the tasks created by asynchronous + // startup are still running. void CreateStartupTasks(); // Perform the default message loop run logic. @@ -86,6 +92,9 @@ class CONTENT_EXPORT BrowserMainLoop { MediaStreamManager* media_stream_manager() const { return media_stream_manager_.get(); } + media::UserInputMonitor* user_input_monitor() const { + return user_input_monitor_.get(); + } media::MIDIManager* midi_manager() const { return midi_manager_.get(); } base::Thread* indexed_db_thread() const { return indexed_db_thread_.get(); } @@ -122,6 +131,8 @@ class CONTENT_EXPORT BrowserMainLoop { scoped_ptr<base::PowerMonitor> power_monitor_; scoped_ptr<base::HighResolutionTimerManager> hi_res_timer_manager_; scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; + // user_input_monitor_ has to outlive audio_manager_, so declared first. + scoped_ptr<media::UserInputMonitor> user_input_monitor_; scoped_ptr<media::AudioManager> audio_manager_; scoped_ptr<media::MIDIManager> midi_manager_; scoped_ptr<AudioMirroringManager> audio_mirroring_manager_; @@ -135,6 +146,8 @@ class CONTENT_EXPORT BrowserMainLoop { #elif defined(OS_MACOSX) && !defined(OS_IOS) scoped_ptr<DeviceMonitorMac> device_monitor_mac_; #endif + // The startup task runner is created by CreateStartupTasks() + scoped_ptr<StartupTaskRunner> startup_task_runner_; // Destroy parts_ before main_message_loop_ (required) and before other // classes constructed in content (but after main_thread_). @@ -158,6 +171,7 @@ class CONTENT_EXPORT BrowserMainLoop { scoped_ptr<base::Thread> indexed_db_thread_; scoped_ptr<MemoryObserver> memory_observer_; scoped_ptr<base::debug::TraceMemoryController> trace_memory_controller_; + scoped_ptr<base::debug::TraceEventSystemStatsMonitor> system_stats_monitor_; DISALLOW_COPY_AND_ASSIGN(BrowserMainLoop); }; diff --git a/chromium/content/browser/browser_main_runner.cc b/chromium/content/browser/browser_main_runner.cc index cca1466d9bb..27fb6fcff58 100644 --- a/chromium/content/browser/browser_main_runner.cc +++ b/chromium/content/browser/browser_main_runner.cc @@ -12,6 +12,7 @@ #include "base/metrics/histogram.h" #include "base/metrics/statistics_recorder.h" #include "content/browser/browser_main_loop.h" +#include "content/browser/browser_shutdown_profile_dumper.h" #include "content/browser/notification_service_impl.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" @@ -29,75 +30,82 @@ namespace content { class BrowserMainRunnerImpl : public BrowserMainRunner { public: - BrowserMainRunnerImpl() : is_initialized_(false), is_shutdown_(false) {} + BrowserMainRunnerImpl() + : initialization_started_(false), is_shutdown_(false) {} virtual ~BrowserMainRunnerImpl() { - if (is_initialized_ && !is_shutdown_) + if (initialization_started_ && !is_shutdown_) Shutdown(); } - virtual int Initialize(const MainFunctionParams& parameters) - OVERRIDE { - TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize") - is_initialized_ = true; + virtual int Initialize(const MainFunctionParams& parameters) OVERRIDE { + TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize"); + // On Android we normally initialize the browser in a series of UI thread + // tasks. While this is happening a second request can come from the OS or + // another application to start the browser. If this happens then we must + // not run these parts of initialization twice. + if (!initialization_started_) { + initialization_started_ = true; #if !defined(OS_IOS) - if (parameters.command_line.HasSwitch(switches::kWaitForDebugger)) - base::debug::WaitForDebugger(60, true); + if (parameters.command_line.HasSwitch(switches::kWaitForDebugger)) + base::debug::WaitForDebugger(60, true); #endif #if defined(OS_WIN) - if (parameters.command_line.HasSwitch( - switches::kEnableTextServicesFramework)) { - base::win::SetForceToUseTSF(); - } else if (base::win::GetVersion() < base::win::VERSION_VISTA) { - // When "Extend support of advanced text services to all programs" - // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on - // Windows XP and handwriting modules shipped with Office 2003 are - // installed, "penjpn.dll" and "skchui.dll" will be loaded and then crash - // unless a user installs Office 2003 SP3. To prevent these modules from - // being loaded, disable TSF entirely. crbug/160914. - // TODO(yukawa): Add a high-level wrapper for this instead of calling - // Win32 API here directly. - ImmDisableTextFrameService(static_cast<DWORD>(-1)); - } + if (parameters.command_line.HasSwitch( + switches::kEnableTextServicesFramework)) { + base::win::SetForceToUseTSF(); + } else if (base::win::GetVersion() < base::win::VERSION_VISTA) { + // When "Extend support of advanced text services to all programs" + // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on + // Windows XP and handwriting modules shipped with Office 2003 are + // installed, "penjpn.dll" and "skchui.dll" will be loaded and then + // crash + // unless a user installs Office 2003 SP3. To prevent these modules from + // being loaded, disable TSF entirely. crbug/160914. + // TODO(yukawa): Add a high-level wrapper for this instead of calling + // Win32 API here directly. + ImmDisableTextFrameService(static_cast<DWORD>(-1)); + } #endif // OS_WIN - base::StatisticsRecorder::Initialize(); + base::StatisticsRecorder::Initialize(); - notification_service_.reset(new NotificationServiceImpl); + notification_service_.reset(new NotificationServiceImpl); #if defined(OS_WIN) - // Ole must be initialized before starting message pump, so that TSF - // (Text Services Framework) module can interact with the message pump - // on Windows 8 Metro mode. - ole_initializer_.reset(new ui::ScopedOleInitializer); + // Ole must be initialized before starting message pump, so that TSF + // (Text Services Framework) module can interact with the message pump + // on Windows 8 Metro mode. + ole_initializer_.reset(new ui::ScopedOleInitializer); #endif // OS_WIN - main_loop_.reset(new BrowserMainLoop(parameters)); + main_loop_.reset(new BrowserMainLoop(parameters)); - main_loop_->Init(); + main_loop_->Init(); - main_loop_->EarlyInitialization(); + main_loop_->EarlyInitialization(); - // Must happen before we try to use a message loop or display any UI. - main_loop_->InitializeToolkit(); + // Must happen before we try to use a message loop or display any UI. + main_loop_->InitializeToolkit(); - main_loop_->MainMessageLoopStart(); + main_loop_->MainMessageLoopStart(); - // WARNING: If we get a WM_ENDSESSION, objects created on the stack here - // are NOT deleted. If you need something to run during WM_ENDSESSION add it - // to browser_shutdown::Shutdown or BrowserProcess::EndSession. +// WARNING: If we get a WM_ENDSESSION, objects created on the stack here +// are NOT deleted. If you need something to run during WM_ENDSESSION add it +// to browser_shutdown::Shutdown or BrowserProcess::EndSession. #if defined(OS_WIN) && !defined(NO_TCMALLOC) - // When linking shared libraries, NO_TCMALLOC is defined, and dynamic - // allocator selection is not supported. + // When linking shared libraries, NO_TCMALLOC is defined, and dynamic + // allocator selection is not supported. - // Make this call before going multithreaded, or spawning any subprocesses. - base::allocator::SetupSubprocessAllocator(); + // Make this call before going multithreaded, or spawning any + // subprocesses. + base::allocator::SetupSubprocessAllocator(); #endif - ui::InitializeInputMethod(); - + ui::InitializeInputMethod(); + } main_loop_->CreateStartupTasks(); int result_code = main_loop_->GetResultCode(); if (result_code > 0) @@ -108,34 +116,46 @@ class BrowserMainRunnerImpl : public BrowserMainRunner { } virtual int Run() OVERRIDE { - DCHECK(is_initialized_); + DCHECK(initialization_started_); DCHECK(!is_shutdown_); main_loop_->RunMainMessageLoopParts(); return main_loop_->GetResultCode(); } virtual void Shutdown() OVERRIDE { - DCHECK(is_initialized_); + DCHECK(initialization_started_); DCHECK(!is_shutdown_); - g_exited_main_message_loop = true; + // The shutdown tracing got enabled in AttemptUserExit earlier, but someone + // needs to write the result to disc. For that a dumper needs to get created + // which will dump the traces to disc when it gets destroyed. + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + scoped_ptr<BrowserShutdownProfileDumper> profiler; + if (command_line.HasSwitch(switches::kTraceShutdown)) + profiler.reset(new BrowserShutdownProfileDumper()); - main_loop_->ShutdownThreadsAndCleanUp(); + { + // The trace event has to stay between profiler creation and destruction. + TRACE_EVENT0("shutdown", "BrowserMainRunner"); + g_exited_main_message_loop = true; - ui::ShutdownInputMethod(); -#if defined(OS_WIN) - ole_initializer_.reset(NULL); -#endif + main_loop_->ShutdownThreadsAndCleanUp(); + + ui::ShutdownInputMethod(); + #if defined(OS_WIN) + ole_initializer_.reset(NULL); + #endif - main_loop_.reset(NULL); + main_loop_.reset(NULL); - notification_service_.reset(NULL); + notification_service_.reset(NULL); - is_shutdown_ = true; + is_shutdown_ = true; + } } protected: - // True if the runner has been initialized. - bool is_initialized_; + // True if we have started to initialize the runner. + bool initialization_started_; // True if the runner has been shut down. bool is_shutdown_; diff --git a/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc index 4a06aca32c9..1f673ba520f 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc @@ -188,7 +188,8 @@ void BrowserPluginEmbedder::OnAttach( guest->GetWebContents(), web_contents(), extra_params); - guest->Attach(static_cast<WebContentsImpl*>(web_contents()), params); + guest->Attach( + static_cast<WebContentsImpl*>(web_contents()), params, extra_params); return; } diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc index 5f1ec69e8af..19e7eba3d44 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc @@ -6,7 +6,6 @@ #include <algorithm> -#include "base/command_line.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -15,6 +14,7 @@ #include "content/browser/browser_plugin/browser_plugin_guest_manager.h" #include "content/browser/browser_plugin/browser_plugin_host_factory.h" #include "content/browser/browser_thread_impl.h" +#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" @@ -38,13 +38,14 @@ #include "content/public/browser/resource_request_details.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents_view.h" -#include "content/public/common/content_switches.h" #include "content/public/common/drop_data.h" #include "content/public/common/media_stream_request.h" #include "content/public/common/result_codes.h" +#include "content/public/common/url_constants.h" +#include "content/public/common/url_utils.h" #include "net/url_request/url_request.h" #include "third_party/WebKit/public/web/WebCursorInfo.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/surface/transport_dib.h" #include "webkit/common/resource_type.h" @@ -127,7 +128,7 @@ class BrowserPluginGuest::GeolocationRequest : public PermissionRequest { // in the fact whether the embedder/app has geolocation // permission. Therefore we use an invalid |bridge_id|. -1 /* bridge_id */, - web_contents->GetURL(), + web_contents->GetLastCommittedURL(), geolocation_callback); return; } @@ -317,8 +318,7 @@ class BrowserPluginGuest::EmbedderRenderViewHostObserver // RenderViewHostObserver: virtual void RenderViewHostDestroyed( RenderViewHost* render_view_host) OVERRIDE { - browser_plugin_guest_->embedder_web_contents_ = NULL; - browser_plugin_guest_->Destroy(); + browser_plugin_guest_->EmbedderDestroyed(); } private: @@ -348,6 +348,7 @@ BrowserPluginGuest::BrowserPluginGuest( embedder_visible_(true), next_permission_request_id_(browser_plugin::kInvalidPermissionRequestID), has_render_view_(has_render_view), + last_seen_auto_size_enabled_(false), is_in_destruction_(false) { DCHECK(web_contents); web_contents->SetDelegate(this); @@ -421,6 +422,54 @@ int BrowserPluginGuest::RequestPermission( return request_id; } +BrowserPluginGuest* BrowserPluginGuest::CreateNewGuestWindow( + const OpenURLParams& params) { + BrowserPluginGuestManager* guest_manager = + GetWebContents()->GetBrowserPluginGuestManager(); + + // Allocate a new instance ID for the new guest. + int instance_id = guest_manager->get_next_instance_id(); + + // Set the attach params to use the same partition as the opener. + // We pull the partition information from the site's URL, which is of the form + // guest://site/{persist}?{partition_name}. + const GURL& site_url = GetWebContents()->GetSiteInstance()->GetSiteURL(); + BrowserPluginHostMsg_Attach_Params attach_params; + attach_params.storage_partition_id = site_url.query(); + attach_params.persist_storage = + site_url.path().find("persist") != std::string::npos; + + // The new guest gets a copy of this guest's extra params so that the content + // embedder exposes the same API for this guest as its opener. + scoped_ptr<base::DictionaryValue> extra_params( + extra_attach_params_->DeepCopy()); + BrowserPluginGuest* new_guest = + GetWebContents()->GetBrowserPluginGuestManager()->CreateGuest( + GetWebContents()->GetSiteInstance(), instance_id, + attach_params, extra_params.Pass()); + new_guest->opener_ = AsWeakPtr(); + + // Take ownership of |new_guest|. + pending_new_windows_.insert( + std::make_pair(new_guest, NewWindowInfo(params.url, std::string()))); + + // Request permission to show the new window. + RequestNewWindowPermission( + new_guest->GetWebContents(), + params.disposition, + gfx::Rect(), + params.user_gesture); + + return new_guest; +} + +void BrowserPluginGuest::EmbedderDestroyed() { + embedder_web_contents_ = NULL; + if (delegate_) + delegate_->EmbedderDestroyed(); + Destroy(); +} + void BrowserPluginGuest::Destroy() { is_in_destruction_ = true; if (!attached() && opener()) @@ -503,6 +552,8 @@ void BrowserPluginGuest::Initialize( // Navigation is disabled in Chrome Apps. We want to make sure guest-initiated // navigations still continue to function inside the app. renderer_prefs->browser_handles_all_top_level_requests = false; + // Disable "client blocked" error page for browser plugin. + renderer_prefs->disable_client_blocked_error_page = true; // Listen to embedder visibility changes so that the guest is in a 'shown' // state if both the embedder is visible and the BrowserPlugin is marked as @@ -543,6 +594,18 @@ void BrowserPluginGuest::Initialize( GetWebContents()->GetRenderViewHost()); guest_rvh->SetInputMethodActive(true); } + + // Inform the embedder of the guest's information. + // We pull the partition information from the site's URL, which is of the form + // guest://site/{persist}?{partition_name}. + const GURL& site_url = GetWebContents()->GetSiteInstance()->GetSiteURL(); + BrowserPluginMsg_Attach_ACK_Params ack_params; + ack_params.storage_partition_id = site_url.query(); + ack_params.persist_storage = + site_url.path().find("persist") != std::string::npos; + ack_params.name = name_; + SendMessageToEmbedder( + new BrowserPluginMsg_Attach_ACK(instance_id_, ack_params)); } BrowserPluginGuest::~BrowserPluginGuest() { @@ -564,6 +627,7 @@ BrowserPluginGuest* BrowserPluginGuest::Create( } else { guest = new BrowserPluginGuest(instance_id, web_contents, NULL, false); } + guest->extra_attach_params_.reset(extra_params->DeepCopy()); web_contents->SetBrowserPluginGuest(guest); BrowserPluginGuestDelegate* delegate = NULL; GetContentClient()->browser()->GuestWebContentsCreated( @@ -654,6 +718,12 @@ void BrowserPluginGuest::CanDownload( callback)); } +void BrowserPluginGuest::LoadProgressChanged(WebContents* contents, + double progress) { + if (delegate_) + delegate_->LoadProgressed(progress); +} + void BrowserPluginGuest::CloseContents(WebContents* source) { if (!delegate_) return; @@ -711,10 +781,14 @@ WebContents* BrowserPluginGuest::OpenURLFromTab(WebContents* source, it->second = new_window_info; return NULL; } - // This can happen for cross-site redirects. - source->GetController().LoadURL( - params.url, params.referrer, params.transition, std::string()); - return source; + if (params.disposition == CURRENT_TAB) { + // This can happen for cross-site redirects. + source->GetController().LoadURL( + params.url, params.referrer, params.transition, std::string()); + return source; + } + + return CreateNewGuestWindow(params)->GetWebContents(); } void BrowserPluginGuest::WebContentsCreated(WebContents* source_contents, @@ -766,6 +840,10 @@ WebContentsImpl* BrowserPluginGuest::GetWebContents() { base::SharedMemory* BrowserPluginGuest::GetDamageBufferFromEmbedder( const BrowserPluginHostMsg_ResizeGuest_Params& params) { + if (!attached()) { + LOG(WARNING) << "Attempting to map a damage buffer prior to attachment."; + return NULL; + } #if defined(OS_WIN) base::ProcessHandle handle = embedder_web_contents_->GetRenderProcessHost()->GetHandle(); @@ -961,9 +1039,8 @@ void BrowserPluginGuest::DidCommitProvisionalLoadForFrame( } void BrowserPluginGuest::DidStopLoading(RenderViewHost* render_view_host) { - bool disable_dragdrop = !CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserPluginDragDrop); - if (disable_dragdrop) { + bool enable_dragdrop = delegate_ && delegate_->IsDragAndDropEnabled(); + if (!enable_dragdrop) { // Initiating a drag from inside a guest is currently not supported without // the kEnableBrowserPluginDragDrop flag on a linux platform. So inject some // JS to disable it. http://crbug.com/161112 @@ -1080,10 +1157,13 @@ bool BrowserPluginGuest::OnMessageReceived(const IPC::Message& message) { void BrowserPluginGuest::Attach( WebContentsImpl* embedder_web_contents, - BrowserPluginHostMsg_Attach_Params params) { + BrowserPluginHostMsg_Attach_Params params, + const base::DictionaryValue& extra_params) { if (attached()) return; + extra_attach_params_.reset(extra_params.DeepCopy()); + // Clear parameters that get inherited from the opener. params.storage_partition_id.clear(); params.persist_storage = false; @@ -1125,18 +1205,6 @@ void BrowserPluginGuest::Attach( Initialize(embedder_web_contents, params); - // Inform the embedder of the guest's information. - // We pull the partition information from the site's URL, which is of the form - // guest://site/{persist}?{partition_name}. - const GURL& site_url = GetWebContents()->GetSiteInstance()->GetSiteURL(); - BrowserPluginMsg_Attach_ACK_Params ack_params; - ack_params.storage_partition_id = site_url.query(); - ack_params.persist_storage = - site_url.path().find("persist") != std::string::npos; - ack_params.name = name_; - SendMessageToEmbedder( - new BrowserPluginMsg_Attach_ACK(instance_id_, ack_params)); - SendQueuedMessages(); RecordAction(UserMetricsAction("BrowserPlugin.Guest.Attached")); @@ -1261,7 +1329,7 @@ void BrowserPluginGuest::OnLockMouse(bool user_gesture, base::Value::CreateBooleanValue(last_unlocked_by_target)); request_info.Set(browser_plugin::kURL, base::Value::CreateStringValue( - web_contents()->GetURL().spec())); + web_contents()->GetLastCommittedURL().spec())); RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK, new PointerLockRequest(this), @@ -1285,13 +1353,31 @@ void BrowserPluginGuest::OnNavigateGuest( // should never be sent to BrowserPluginGuest (browser process). DCHECK(!src.empty()); if (!src.empty()) { + // Do not allow navigating a guest to schemes other than known safe schemes. + // This will block the embedder trying to load unwanted schemes, e.g. + // chrome://settings. + bool scheme_is_blocked = + (!ChildProcessSecurityPolicyImpl::GetInstance()->IsWebSafeScheme( + url.scheme()) && + !ChildProcessSecurityPolicyImpl::GetInstance()->IsPseudoScheme( + url.scheme())) || + url.SchemeIs(kJavaScriptScheme); + if (scheme_is_blocked || !url.is_valid()) { + if (delegate_) { + std::string error_type; + RemoveChars(net::ErrorToString(net::ERR_ABORTED), "net::", &error_type); + delegate_->LoadAbort(true /* is_top_level */, url, error_type); + } + return; + } + // As guests do not swap processes on navigation, only navigations to // normal web URLs are supported. No protocol handlers are installed for // other schemes (e.g., WebUI or extensions), and no permissions or bindings // can be granted to the guest process. GetWebContents()->GetController().LoadURL(url, Referrer(), - PAGE_TRANSITION_AUTO_TOPLEVEL, - std::string()); + PAGE_TRANSITION_AUTO_TOPLEVEL, + std::string()); } } @@ -1318,6 +1404,13 @@ void BrowserPluginGuest::OnResizeGuest( render_widget_host->NotifyScreenInfoChanged(); } } + // When autosize is turned off and as a result there is a layout change, we + // send a sizechanged event. + if (!auto_size_enabled_ && last_seen_auto_size_enabled_ && + !params.view_rect.size().IsEmpty() && delegate_) { + delegate_->SizeChanged(last_seen_view_size_, params.view_rect.size()); + last_seen_auto_size_enabled_ = false; + } // Invalid damage buffer means we are in HW compositing mode, // so just resize the WebContents and repaint if needed. if (!base::SharedMemory::IsHandleValid(params.damage_buffer_handle)) { @@ -1362,6 +1455,7 @@ void BrowserPluginGuest::OnSetSize( if (auto_size_enabled_ && (!old_auto_size_enabled || (old_max_size != max_auto_size_) || (old_min_size != min_auto_size_))) { + RecordAction(UserMetricsAction("BrowserPlugin.Guest.EnableAutoResize")); GetWebContents()->GetRenderViewHost()->EnableAutoResize( min_auto_size_, max_auto_size_); // TODO(fsamuel): If we're changing autosize parameters, then we force @@ -1580,6 +1674,16 @@ void BrowserPluginGuest::OnUpdateRect( params.flags); relay_params.needs_ack = params.needs_ack; + bool size_changed = last_seen_view_size_ != params.view_size; + gfx::Size old_size = last_seen_view_size_; + last_seen_view_size_ = params.view_size; + + if ((auto_size_enabled_ || last_seen_auto_size_enabled_) && + size_changed && delegate_) { + delegate_->SizeChanged(old_size, last_seen_view_size_); + } + last_seen_auto_size_enabled_ = auto_size_enabled_; + // HW accelerated case, acknowledge resize only if (!params.needs_ack || !damage_buffer_) { relay_params.damage_buffer_sequence_id = 0; diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.h b/chromium/content/browser/browser_plugin/browser_plugin_guest.h index 6098cacca38..0101ff7118d 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_guest.h +++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.h @@ -104,6 +104,10 @@ class CONTENT_EXPORT BrowserPluginGuest BrowserPluginGuest* opener, bool has_render_view); + // Called when the embedder RenderViewHost is destroyed to give the + // BrowserPluginGuest an opportunity to clean up after itself. + void EmbedderDestroyed(); + // Destroys the guest WebContents and all its associated state, including // this BrowserPluginGuest, and its new unattached windows. void Destroy(); @@ -182,6 +186,8 @@ class CONTENT_EXPORT BrowserPluginGuest int request_id, const std::string& request_method, const base::Callback<void(bool)>& callback) OVERRIDE; + virtual void LoadProgressChanged(WebContents* source, + double progress) OVERRIDE; virtual void CloseContents(WebContents* source) OVERRIDE; virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE; virtual bool HandleContextMenu(const ContextMenuParams& params) OVERRIDE; @@ -248,9 +254,12 @@ class CONTENT_EXPORT BrowserPluginGuest // Attaches this BrowserPluginGuest to the provided |embedder_web_contents| // and initializes the guest with the provided |params|. Attaching a guest // to an embedder implies that this guest's lifetime is no longer managed - // by its opener, and it can begin loading resources. + // by its opener, and it can begin loading resources. |extra_params| are + // parameters passed into BrowserPlugin from JavaScript to be forwarded to + // the content embedder. void Attach(WebContentsImpl* embedder_web_contents, - BrowserPluginHostMsg_Attach_Params params); + BrowserPluginHostMsg_Attach_Params params, + const base::DictionaryValue& extra_params); // Requests geolocation permission through Embedder JavaScript API. void AskEmbedderForGeolocationPermission(int bridge_id, @@ -320,6 +329,10 @@ class CONTENT_EXPORT BrowserPluginGuest scoped_refptr<BrowserPluginGuest::PermissionRequest> request, const base::DictionaryValue& request_info); + // Creates a new guest window, and BrowserPluginGuest that is owned by this + // BrowserPluginGuest. + BrowserPluginGuest* CreateNewGuestWindow(const OpenURLParams& params); + base::SharedMemory* damage_buffer() const { return damage_buffer_.get(); } const gfx::Size& damage_view_size() const { return damage_view_size_; } float damage_buffer_scale_factor() const { @@ -516,6 +529,11 @@ class CONTENT_EXPORT BrowserPluginGuest // this guest is attached. bool has_render_view_; + // Last seen size of guest contents (by OnUpdateRect). + gfx::Size last_seen_view_size_; + // Last seen autosize attribute state (by OnUpdateRect). + bool last_seen_auto_size_enabled_; + bool is_in_destruction_; // This is a queue of messages that are destined to be sent to the embedder @@ -524,6 +542,10 @@ class CONTENT_EXPORT BrowserPluginGuest scoped_ptr<BrowserPluginGuestDelegate> delegate_; + // These are parameters passed from JavaScript on attachment to the content + // embedder. + scoped_ptr<base::DictionaryValue> extra_attach_params_; + DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuest); }; diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc index 75de2031333..6c86331b0fe 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc @@ -17,7 +17,7 @@ #include "content/public/common/result_codes.h" #include "content/public/common/url_constants.h" #include "net/base/escape.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace content { diff --git a/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc b/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc index 4fc2ae80335..d49f169f05f 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc @@ -13,6 +13,7 @@ #include "content/browser/browser_plugin/browser_plugin_host_factory.h" #include "content/browser/browser_plugin/test_browser_plugin_embedder.h" #include "content/browser/browser_plugin/test_browser_plugin_guest.h" +#include "content/browser/browser_plugin/test_browser_plugin_guest_delegate.h" #include "content/browser/browser_plugin/test_browser_plugin_guest_manager.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -24,9 +25,10 @@ #include "content/public/browser/render_widget_host_view.h" #include "content/public/common/content_switches.h" #include "content/public/common/drop_data.h" +#include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/net_util.h" @@ -251,6 +253,9 @@ class BrowserPluginHostTest : public ContentBrowserTest { #if defined(OS_WIN) && !defined(USE_AURA) UseRealGLBindings(); #endif + // We need real contexts, otherwise the embedder doesn't composite, but the + // guest does, and that isn't an expected configuration. + UseRealGLContexts(); ContentBrowserTest::SetUp(); } @@ -763,86 +768,12 @@ IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) { IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) { const char* kEmbedderURL = "/browser_plugin_embedder.html"; StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); - RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( - test_embedder()->web_contents()->GetRenderViewHost()); - RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( - test_guest()->web_contents()->GetRenderViewHost()); - { - // Focus the BrowserPlugin. This will have the effect of also focusing the - // current guest. - ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').focus();"); - // Verify that key presses go to the guest. - SimulateSpaceKeyPress(test_embedder()->web_contents()); - test_guest()->WaitForInput(); - // Verify that the guest is focused. - scoped_ptr<base::Value> value = - content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()"); - bool result = false; - ASSERT_TRUE(value->GetAsBoolean(&result)); - EXPECT_TRUE(result); - } // Blur the embedder. test_embedder()->web_contents()->GetRenderViewHost()->Blur(); + // Ensure that the guest is also blurred. test_guest()->WaitForBlur(); } -// This test verifies that if a browser plugin is in autosize mode before -// navigation then the guest starts auto-sized. -IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AutoSizeBeforeNavigation) { - const char* kEmbedderURL = "/browser_plugin_embedder.html"; - const std::string embedder_code = - "document.getElementById('plugin').minwidth = 300;" - "document.getElementById('plugin').minheight = 200;" - "document.getElementById('plugin').maxwidth = 600;" - "document.getElementById('plugin').maxheight = 400;" - "document.getElementById('plugin').autosize = true;"; - StartBrowserPluginTest( - kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code); - // Verify that the guest has been auto-sized. - test_guest()->WaitForViewSize(gfx::Size(300, 400)); -} - -// This test verifies that enabling autosize resizes the guest and triggers -// a 'sizechanged' event. -IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AutoSizeAfterNavigation) { - const char* kEmbedderURL = "/browser_plugin_embedder.html"; - StartBrowserPluginTest( - kEmbedderURL, kHTMLForGuestWithSize, true, std::string()); - RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( - test_embedder()->web_contents()->GetRenderViewHost()); - - { - const string16 expected_title = ASCIIToUTF16("AutoSize(300, 400)"); - content::TitleWatcher title_watcher(test_embedder()->web_contents(), - expected_title); - ExecuteSyncJSFunction( - rvh, - "document.getElementById('plugin').minwidth = 300;" - "document.getElementById('plugin').minheight = 200;" - "document.getElementById('plugin').maxwidth = 600;" - "document.getElementById('plugin').maxheight = 400;" - "document.getElementById('plugin').autosize = true;"); - string16 actual_title = title_watcher.WaitAndGetTitle(); - EXPECT_EQ(expected_title, actual_title); - } - { - // Change the minwidth and verify that it causes relayout. - const string16 expected_title = ASCIIToUTF16("AutoSize(350, 400)"); - content::TitleWatcher title_watcher(test_embedder()->web_contents(), - expected_title); - ExecuteSyncJSFunction( - rvh, "document.getElementById('plugin').minwidth = 350;"); - string16 actual_title = title_watcher.WaitAndGetTitle(); - EXPECT_EQ(expected_title, actual_title); - } - { - // Turn off autoSize and verify that the guest resizes to fit the container. - ExecuteSyncJSFunction( - rvh, "document.getElementById('plugin').autosize = null;"); - test_guest()->WaitForViewSize(gfx::Size(640, 480)); - } -} - // Test for regression http://crbug.com/162961. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) { const char kEmbedderURL[] = "/browser_plugin_embedder.html"; @@ -858,79 +789,6 @@ IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) { test_embedder()->last_rvh_at_position_response()); } -// This test verifies that all autosize attributes can be removed -// without crashing the plugin, or throwing errors. -IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, RemoveAutosizeAttributes) { - const char* kEmbedderURL = "/browser_plugin_embedder.html"; - const std::string embedder_code = - "document.getElementById('plugin').minwidth = 300;" - "document.getElementById('plugin').minheight = 200;" - "document.getElementById('plugin').maxwidth = 600;" - "document.getElementById('plugin').maxheight = 400;" - "document.getElementById('plugin').name = 'name';" - "document.getElementById('plugin').src = 'foo';" - "document.getElementById('plugin').autosize = '';"; - StartBrowserPluginTest( - kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code); - RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( - test_embedder()->web_contents()->GetRenderViewHost()); - RemoveAttributes(rvh, "maxheight, maxwidth, minheight, minwidth, autosize"); - - // Verify that the guest resizes to fit the container (and hasn't crashed). - test_guest()->WaitForViewSize(gfx::Size(640, 480)); - EXPECT_TRUE(IsAttributeNull(rvh, "maxheight")); - EXPECT_TRUE(IsAttributeNull(rvh, "maxwidth")); - EXPECT_TRUE(IsAttributeNull(rvh, "minheight")); - EXPECT_TRUE(IsAttributeNull(rvh, "minwidth")); - EXPECT_TRUE(IsAttributeNull(rvh, "autosize")); -} - -// This test verifies that autosize works when some of the parameters are unset. -IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PartialAutosizeAttributes) { - const char* kEmbedderURL = "/browser_plugin_embedder.html"; - const std::string embedder_code = - "document.getElementById('plugin').minwidth = 300;" - "document.getElementById('plugin').minheight = 200;" - "document.getElementById('plugin').maxwidth = 700;" - "document.getElementById('plugin').maxheight = 600;" - "document.getElementById('plugin').autosize = '';"; - StartBrowserPluginTest( - kEmbedderURL, kHTMLForGuestWithSize, true, embedder_code); - RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( - test_embedder()->web_contents()->GetRenderViewHost()); - { - // Remove an autosize attribute and verify that it causes relayout. - const string16 expected_title = ASCIIToUTF16("AutoSize(640, 400)"); - content::TitleWatcher title_watcher(test_embedder()->web_contents(), - expected_title); - RemoveAttributes(rvh, "minwidth"); - string16 actual_title = title_watcher.WaitAndGetTitle(); - EXPECT_EQ(expected_title, actual_title); - } - { - // Remove an autosize attribute and verify that it causes relayout. - // Also tests that when minwidth > maxwidth, minwidth = maxwidth. - const string16 expected_title = ASCIIToUTF16("AutoSize(700, 480)"); - content::TitleWatcher title_watcher(test_embedder()->web_contents(), - expected_title); - RemoveAttributes(rvh, "maxheight"); - ExecuteSyncJSFunction( - rvh, "document.getElementById('plugin').minwidth = 800;" - "document.getElementById('plugin').minheight = 800;"); - string16 actual_title = title_watcher.WaitAndGetTitle(); - EXPECT_EQ(expected_title, actual_title); - } - { - // Remove maxwidth and make sure the size returns to plugin size. - const string16 expected_title = ASCIIToUTF16("AutoSize(640, 480)"); - content::TitleWatcher title_watcher(test_embedder()->web_contents(), - expected_title); - RemoveAttributes(rvh, "maxwidth"); - string16 actual_title = title_watcher.WaitAndGetTitle(); - EXPECT_EQ(expected_title, actual_title); - } -} - // This test verifies that if IME is enabled in the embedder, it is also enabled // in the guest. IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) { @@ -941,4 +799,34 @@ IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) { EXPECT_TRUE(rvh->input_method_active()); } +// Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause +// a crash. +IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DoNotCrashOnInvalidNavigation) { + const char kEmbedderURL[] = "/browser_plugin_embedder.html"; + StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string()); + TestBrowserPluginGuestDelegate* delegate = + new TestBrowserPluginGuestDelegate(); + test_guest()->SetDelegate(delegate); + + const char kValidSchemeWithEmptyURL[] = "http:"; + ExecuteSyncJSFunction( + test_embedder()->web_contents()->GetRenderViewHost(), + base::StringPrintf("SetSrc('%s');", kValidSchemeWithEmptyURL)); + EXPECT_TRUE(delegate->load_aborted()); + EXPECT_FALSE(delegate->load_aborted_url().is_valid()); + EXPECT_EQ(kValidSchemeWithEmptyURL, + delegate->load_aborted_url().possibly_invalid_spec()); + + delegate->ResetStates(); + + // Attempt a navigation to chrome-guest://abc123, which is a valid URL. But it + // should be blocked because the scheme isn't web-safe or a pseudo-scheme. + ExecuteSyncJSFunction( + test_embedder()->web_contents()->GetRenderViewHost(), + base::StringPrintf("SetSrc('%s://abc123');", + chrome::kGuestScheme)); + EXPECT_TRUE(delegate->load_aborted()); + EXPECT_TRUE(delegate->load_aborted_url().is_valid()); +} + } // namespace content diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc new file mode 100644 index 00000000000..28c27b7578e --- /dev/null +++ b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc @@ -0,0 +1,66 @@ +// Copyright 2013 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/browser/browser_plugin/test_browser_plugin_guest_delegate.h" + +namespace content { + +TestBrowserPluginGuestDelegate::TestBrowserPluginGuestDelegate() + : load_aborted_(false) { +} + +TestBrowserPluginGuestDelegate::~TestBrowserPluginGuestDelegate() { +} + +void TestBrowserPluginGuestDelegate::ResetStates() { + load_aborted_ = false; + load_aborted_url_ = GURL(); +} + +void TestBrowserPluginGuestDelegate::AddMessageToConsole( + int32 level, + const string16& message, + int32 line_no, + const string16& source_id) { +} + +void TestBrowserPluginGuestDelegate::Close() { +} + +void TestBrowserPluginGuestDelegate::GuestProcessGone( + base::TerminationStatus status) { +} + +bool TestBrowserPluginGuestDelegate::HandleKeyboardEvent( + const NativeWebKeyboardEvent& event) { + return BrowserPluginGuestDelegate::HandleKeyboardEvent(event); +} + +void TestBrowserPluginGuestDelegate::LoadAbort(bool is_top_level, + const GURL& url, + const std::string& error_type) { + load_aborted_ = true; + load_aborted_url_ = url; +} + +void TestBrowserPluginGuestDelegate::RendererResponsive() { +} + +void TestBrowserPluginGuestDelegate::RendererUnresponsive() { +} + +bool TestBrowserPluginGuestDelegate::RequestPermission( + BrowserPluginPermissionType permission_type, + const base::DictionaryValue& request_info, + const PermissionResponseCallback& callback) { + return BrowserPluginGuestDelegate::RequestPermission(permission_type, + request_info, + callback); +} + +void TestBrowserPluginGuestDelegate::SizeChanged(const gfx::Size& old_size, + const gfx::Size& new_size) { +} + +} // namespace content diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h new file mode 100644 index 00000000000..b6e7e9079f3 --- /dev/null +++ b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h @@ -0,0 +1,51 @@ +// Copyright 2013 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_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_ +#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_ + +#include "content/public/browser/browser_plugin_guest_delegate.h" + +namespace content { + +class TestBrowserPluginGuestDelegate : public BrowserPluginGuestDelegate { + public: + TestBrowserPluginGuestDelegate(); + virtual ~TestBrowserPluginGuestDelegate(); + + void ResetStates(); + + bool load_aborted() const { return load_aborted_; } + const GURL& load_aborted_url() const { return load_aborted_url_; } + + private: + // Overridden from BrowserPluginGuestDelegate: + virtual void AddMessageToConsole(int32 level, + const string16& message, + int32 line_no, + const string16& source_id) OVERRIDE; + virtual void Close() OVERRIDE; + virtual void GuestProcessGone(base::TerminationStatus status) OVERRIDE; + virtual bool HandleKeyboardEvent( + const NativeWebKeyboardEvent& event) OVERRIDE; + virtual void LoadAbort(bool is_top_level, + const GURL& url, + const std::string& error_type) OVERRIDE; + virtual void RendererResponsive() OVERRIDE; + virtual void RendererUnresponsive() OVERRIDE; + virtual bool RequestPermission( + BrowserPluginPermissionType permission_type, + const base::DictionaryValue& request_info, + const PermissionResponseCallback& callback) OVERRIDE; + virtual void SizeChanged(const gfx::Size& old_size, + const gfx::Size& new_size) OVERRIDE; + + bool load_aborted_; + GURL load_aborted_url_; + + DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuestDelegate); +}; + +} // namespace content +#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_ diff --git a/chromium/content/browser/browser_shutdown_profile_dumper.cc b/chromium/content/browser/browser_shutdown_profile_dumper.cc new file mode 100644 index 00000000000..70e3e5389c1 --- /dev/null +++ b/chromium/content/browser/browser_shutdown_profile_dumper.cc @@ -0,0 +1,139 @@ +// Copyright 2013 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/browser/browser_shutdown_profile_dumper.h" + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug/trace_event.h" +#include "base/debug/trace_event_impl.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#include "content/public/common/content_switches.h" + +namespace content { + +BrowserShutdownProfileDumper::BrowserShutdownProfileDumper() + : blocks_(0), + dump_file_(NULL) { +} + +BrowserShutdownProfileDumper::~BrowserShutdownProfileDumper() { + WriteTracesToDisc(GetFileName()); +} + +void BrowserShutdownProfileDumper::WriteTracesToDisc( + const base::FilePath& file_name) { + // Note: I have seen a usage of 0.000xx% when dumping - which fits easily. + // Since the tracer stops when the trace buffer is filled, we'd rather save + // what we have than nothing since we might see from the amount of events + // that caused the problem. + DVLOG(1) << "Flushing shutdown traces to disc. The buffer is %" << + base::debug::TraceLog::GetInstance()->GetBufferPercentFull() << + " full."; + DCHECK(!dump_file_); + dump_file_ = file_util::OpenFile(file_name, "w+"); + if (!IsFileValid()) { + LOG(ERROR) << "Failed to open performance trace file: " << + file_name.value(); + return; + } + WriteString("{\"traceEvents\":"); + WriteString("["); + + // TraceLog::Flush() requires the calling thread to have a message loop. + // As the message loop of the current thread may have quit, start another + // thread for flushing the trace. + base::WaitableEvent flush_complete_event(false, false); + base::Thread flush_thread("browser_shutdown_trace_event_flush"); + flush_thread.Start(); + flush_thread.message_loop()->PostTask( + FROM_HERE, + base::Bind(&BrowserShutdownProfileDumper::EndTraceAndFlush, + base::Unretained(this), + base::Unretained(&flush_complete_event))); + + bool original_wait_allowed = base::ThreadRestrictions::SetWaitAllowed(true); + flush_complete_event.Wait(); + base::ThreadRestrictions::SetWaitAllowed(original_wait_allowed); +} + +void BrowserShutdownProfileDumper::EndTraceAndFlush( + base::WaitableEvent* flush_complete_event) { + while (base::debug::TraceLog::GetInstance()->IsEnabled()) + base::debug::TraceLog::GetInstance()->SetDisabled(); + base::debug::TraceLog::GetInstance()->Flush( + base::Bind(&BrowserShutdownProfileDumper::WriteTraceDataCollected, + base::Unretained(this), + base::Unretained(flush_complete_event))); +} + +base::FilePath BrowserShutdownProfileDumper::GetFileName() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + base::FilePath trace_file = + command_line.GetSwitchValuePath(switches::kTraceShutdownFile); + + if (!trace_file.empty()) + return trace_file; + + // Default to saving the startup trace into the current dir. + return base::FilePath().AppendASCII("chrometrace.log"); +} + +void BrowserShutdownProfileDumper::WriteTraceDataCollected( + base::WaitableEvent* flush_complete_event, + const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events) { + if (!IsFileValid()) { + flush_complete_event->Signal(); + return; + } + if (blocks_) { + // Blocks are not comma separated. Beginning with the second block we + // start therefore to add one in front of the previous block. + WriteString(","); + } + ++blocks_; + WriteString(events_str->data()); + + if (!has_more_events) { + WriteString("]"); + WriteString("}"); + CloseFile(); + flush_complete_event->Signal(); + } +} + +bool BrowserShutdownProfileDumper::IsFileValid() { + return dump_file_ && (ferror(dump_file_) == 0); +} + +void BrowserShutdownProfileDumper::WriteString(const std::string& string) { + WriteChars(string.data(), string.size()); +} + +void BrowserShutdownProfileDumper::WriteChars(const char* chars, size_t size) { + if (!IsFileValid()) + return; + + size_t written = fwrite(chars, 1, size, dump_file_); + if (written != size) { + LOG(ERROR) << "Error " << ferror(dump_file_) << + " in fwrite() to trace file"; + CloseFile(); + } +} + +void BrowserShutdownProfileDumper::CloseFile() { + if (!dump_file_) + return; + file_util::CloseFile(dump_file_); + dump_file_ = NULL; +} + +} // namespace content diff --git a/chromium/content/browser/browser_shutdown_profile_dumper.h b/chromium/content/browser/browser_shutdown_profile_dumper.h new file mode 100644 index 00000000000..f98a32cb9ca --- /dev/null +++ b/chromium/content/browser/browser_shutdown_profile_dumper.h @@ -0,0 +1,74 @@ +// Copyright 2013 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_BROWSER_BROWSER_SHUTDOWN_PROFILE_DUMPER_H_ +#define CONTENT_BROWSER_BROWSER_SHUTDOWN_PROFILE_DUMPER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "content/common/content_export.h" + +namespace base { +class FilePath; +class WaitableEvent; +} + +namespace content { + +// This class is intended to dump the tracing results of the shutdown process +// to a file before the browser process exits. +// It will save the file either into the command line passed +// "--trace-shutdown-file=<name>" parameter - or - to "chrometrace.log" in the +// current directory. +// Use the class with a scoped_ptr to get files written in the destructor. +// Note that we cannot use the asynchronous file writer since the +// |SequencedWorkerPool| will get killed in the shutdown process. +class BrowserShutdownProfileDumper { + public: + BrowserShutdownProfileDumper(); + + ~BrowserShutdownProfileDumper(); + + private: + // Writes all traces which happened to disk. + void WriteTracesToDisc(const base::FilePath& file_name); + + void EndTraceAndFlush(base::WaitableEvent* flush_complete_event); + + // Returns the file name where we should save the trace dump to. + base::FilePath GetFileName(); + + // The callback for the |TraceLog::Flush| function. It saves all traces to + // disc. + void WriteTraceDataCollected( + base::WaitableEvent* flush_complete_event, + const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events); + + // Returns true if the dump file is valid. + bool IsFileValid(); + + // Writes a string to the dump file. + void WriteString(const std::string& string); + + // Write a buffer to the dump file. + void WriteChars(const char* chars, size_t size); + + // Closes the dump file. + void CloseFile(); + + // The number of blocks we have already written. + int blocks_; + // For dumping the content to disc. + FILE* dump_file_; + + DISALLOW_COPY_AND_ASSIGN(BrowserShutdownProfileDumper); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BROWSER_SHUTDOWN_PROFILE_DUMPER_H_ diff --git a/chromium/content/browser/browser_thread_impl.h b/chromium/content/browser/browser_thread_impl.h index b16bb362351..167cc3b85a1 100644 --- a/chromium/content/browser/browser_thread_impl.h +++ b/chromium/content/browser/browser_thread_impl.h @@ -60,6 +60,7 @@ class CONTENT_EXPORT BrowserThreadImpl : public BrowserThread, // For testing. friend class ContentTestSuiteBaseListener; + friend class TestBrowserThreadBundle; static void FlushThreadPoolHelper(); // The identifier of this thread. Only one thread can exist with a given diff --git a/chromium/content/browser/browser_url_handler_impl.cc b/chromium/content/browser/browser_url_handler_impl.cc index 56efe6b71b7..5ff1309f1a8 100644 --- a/chromium/content/browser/browser_url_handler_impl.cc +++ b/chromium/content/browser/browser_url_handler_impl.cc @@ -13,8 +13,7 @@ namespace content { // Handles rewriting view-source URLs for what we'll actually load. -static bool HandleViewSource(GURL* url, - BrowserContext* browser_context) { +static bool HandleViewSource(GURL* url, BrowserContext* browser_context) { if (url->SchemeIs(kViewSourceScheme)) { // Load the inner URL instead. *url = GURL(url->path()); @@ -22,7 +21,7 @@ static bool HandleViewSource(GURL* url, // Bug 26129: limit view-source to view the content and not any // other kind of 'active' url scheme like 'javascript' or 'data'. static const char* const allowed_sub_schemes[] = { - chrome::kHttpScheme, chrome::kHttpsScheme, chrome::kFtpScheme, + kHttpScheme, kHttpsScheme, chrome::kFtpScheme, chrome::kChromeDevToolsScheme, chrome::kChromeUIScheme, chrome::kFileScheme, chrome::kFileSystemScheme }; diff --git a/chromium/content/browser/byte_stream.cc b/chromium/content/browser/byte_stream.cc index a8b8f29e9e6..7b0f9fb0f9a 100644 --- a/chromium/content/browser/byte_stream.cc +++ b/chromium/content/browser/byte_stream.cc @@ -61,6 +61,7 @@ class ByteStreamWriterImpl : public ByteStreamWriter { virtual void Flush() OVERRIDE; virtual void Close(int status) OVERRIDE; virtual void RegisterCallback(const base::Closure& source_callback) OVERRIDE; + virtual size_t GetTotalBufferedBytes() const OVERRIDE; // PostTask target from |ByteStreamReaderImpl::MaybeUpdateInput|. static void UpdateWindow(scoped_refptr<LifetimeFlag> lifetime_flag, @@ -209,6 +210,18 @@ bool ByteStreamWriterImpl::Write( scoped_refptr<net::IOBuffer> buffer, size_t byte_count) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); + // Check overflow. + // + // TODO(tyoshino): Discuss with content/browser/download developer and if + // they're fine with, set smaller limit and make it configurable. + size_t space_limit = std::numeric_limits<size_t>::max() - + GetTotalBufferedBytes(); + if (byte_count > space_limit) { + // TODO(tyoshino): Tell the user that Write() failed. + // Ignore input. + return false; + } + input_contents_.push_back(std::make_pair(buffer, byte_count)); input_contents_size_ += byte_count; @@ -216,7 +229,7 @@ bool ByteStreamWriterImpl::Write( if (input_contents_size_ > total_buffer_size_ / kFractionBufferBeforeSending) PostToPeer(false, 0); - return (input_contents_size_ + output_size_used_ <= total_buffer_size_); + return GetTotalBufferedBytes() <= total_buffer_size_; } void ByteStreamWriterImpl::Flush() { @@ -236,6 +249,13 @@ void ByteStreamWriterImpl::RegisterCallback( space_available_callback_ = source_callback; } +size_t ByteStreamWriterImpl::GetTotalBufferedBytes() const { + DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); + // This sum doesn't overflow since Write() fails if this sum is going to + // overflow. + return input_contents_size_ + output_size_used_; +} + // static void ByteStreamWriterImpl::UpdateWindow( scoped_refptr<LifetimeFlag> lifetime_flag, ByteStreamWriterImpl* target, @@ -248,15 +268,16 @@ void ByteStreamWriterImpl::UpdateWindow( void ByteStreamWriterImpl::UpdateWindowInternal(size_t bytes_consumed) { DCHECK(my_task_runner_->RunsTasksOnCurrentThread()); + + bool was_above_limit = GetTotalBufferedBytes() > total_buffer_size_; + DCHECK_GE(output_size_used_, bytes_consumed); output_size_used_ -= bytes_consumed; // Callback if we were above the limit and we're now <= to it. - size_t total_known_size_used = - input_contents_size_ + output_size_used_; + bool no_longer_above_limit = GetTotalBufferedBytes() <= total_buffer_size_; - if (total_known_size_used <= total_buffer_size_ && - (total_known_size_used + bytes_consumed > total_buffer_size_) && + if (no_longer_above_limit && was_above_limit && !space_available_callback_.is_null()) space_available_callback_.Run(); } diff --git a/chromium/content/browser/byte_stream.h b/chromium/content/browser/byte_stream.h index 0b5640d0711..60b01634c5c 100644 --- a/chromium/content/browser/byte_stream.h +++ b/chromium/content/browser/byte_stream.h @@ -153,6 +153,10 @@ class CONTENT_EXPORT ByteStreamWriter { // or after callbacks may be called). // The callback will not be called after ByteStreamWriter destruction. virtual void RegisterCallback(const base::Closure& source_callback) = 0; + + // Returns the number of bytes sent to the reader but not yet reported by + // the reader as read. + virtual size_t GetTotalBufferedBytes() const = 0; }; class CONTENT_EXPORT ByteStreamReader { diff --git a/chromium/content/browser/byte_stream_unittest.cc b/chromium/content/browser/byte_stream_unittest.cc index 04c5ae37533..f814e2f5d08 100644 --- a/chromium/content/browser/byte_stream_unittest.cc +++ b/chromium/content/browser/byte_stream_unittest.cc @@ -5,6 +5,7 @@ #include "content/browser/byte_stream.h" #include <deque> +#include <limits> #include "base/bind.h" #include "base/callback.h" @@ -116,7 +117,10 @@ TEST_F(ByteStreamTest, ByteStream_PushBack) { EXPECT_FALSE(Write(byte_stream_input.get(), 1024)); // Flush byte_stream_input->Close(0); + EXPECT_EQ(4 * 1024U + 1U, byte_stream_input->GetTotalBufferedBytes()); message_loop_.RunUntilIdle(); + // Data already sent to reader is also counted in. + EXPECT_EQ(4 * 1024U + 1U, byte_stream_input->GetTotalBufferedBytes()); // Pull the IO buffers out; do we get the same buffers and do they // have the same contents? @@ -144,6 +148,10 @@ TEST_F(ByteStreamTest, ByteStream_PushBack) { EXPECT_EQ(ByteStreamReader::STREAM_COMPLETE, byte_stream_output->Read(&output_io_buffer, &output_length)); + + message_loop_.RunUntilIdle(); + // Reader now knows that all data is read out. + EXPECT_EQ(1024U, byte_stream_input->GetTotalBufferedBytes()); } // Confirm that Flush() method makes the writer to send written contents to @@ -579,4 +587,27 @@ TEST_F(ByteStreamTest, ByteStream_FlushWithoutAnyWrite) { byte_stream_output->Read(&output_io_buffer, &output_length)); } +TEST_F(ByteStreamTest, ByteStream_WriteOverflow) { + scoped_ptr<ByteStreamWriter> byte_stream_input; + scoped_ptr<ByteStreamReader> byte_stream_output; + CreateByteStream( + message_loop_.message_loop_proxy(), message_loop_.message_loop_proxy(), + std::numeric_limits<size_t>::max(), + &byte_stream_input, &byte_stream_output); + + EXPECT_TRUE(Write(byte_stream_input.get(), 1)); + // 1 + size_t max -> Overflow. + scoped_refptr<net::IOBuffer> empty_io_buffer; + EXPECT_FALSE(byte_stream_input->Write(empty_io_buffer, + std::numeric_limits<size_t>::max())); + message_loop_.RunUntilIdle(); + + // The first write is below PostToPeer threshold. We shouldn't get anything + // from the output. + scoped_refptr<net::IOBuffer> output_io_buffer; + size_t output_length; + EXPECT_EQ(ByteStreamReader::STREAM_EMPTY, + byte_stream_output->Read(&output_io_buffer, &output_length)); +} + } // namespace content diff --git a/chromium/content/browser/child_process_launcher.cc b/chromium/content/browser/child_process_launcher.cc index b5bb3f491f0..721cd5a88ed 100644 --- a/chromium/content/browser/child_process_launcher.cc +++ b/chromium/content/browser/child_process_launcher.cc @@ -35,6 +35,7 @@ #include "base/memory/singleton.h" #include "content/browser/renderer_host/render_sandbox_host_linux.h" #include "content/browser/zygote_host/zygote_host_impl_linux.h" +#include "content/common/child_process_sandbox_support_impl_linux.h" #endif #if defined(OS_POSIX) @@ -74,7 +75,7 @@ class ChildProcessLauncher::Context int ipcfd, #elif defined(OS_POSIX) bool use_zygote, - const base::EnvironmentVector& environ, + const base::EnvironmentMap& environ, int ipcfd, #endif CommandLine* cmd_line, @@ -186,7 +187,7 @@ class ChildProcessLauncher::Context int ipcfd, #elif defined(OS_POSIX) bool use_zygote, - const base::EnvironmentVector& env, + const base::EnvironmentMap& env, int ipcfd, #endif CommandLine* cmd_line) { @@ -256,13 +257,13 @@ class ChildProcessLauncher::Context RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); fds_to_map.push_back(std::make_pair( sandbox_fd, - kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); + GetSandboxFD())); } #endif // defined(OS_MACOSX) // Actually launch the app. base::LaunchOptions options; - options.environ = &env; + options.environ = env; options.fds_to_remap = &fds_to_map; #if defined(OS_MACOSX) @@ -414,7 +415,7 @@ ChildProcessLauncher::ChildProcessLauncher( SandboxedProcessLauncherDelegate* delegate, #elif defined(OS_POSIX) bool use_zygote, - const base::EnvironmentVector& environ, + const base::EnvironmentMap& environ, int ipcfd, #endif CommandLine* cmd_line, @@ -464,6 +465,11 @@ base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( context_->termination_status_ = ZygoteHostImpl::GetInstance()-> GetTerminationStatus(handle, known_dead, &context_->exit_code_); } else +#elif defined(OS_MACOSX) + if (known_dead) { + context_->termination_status_ = + base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); + } else #endif { context_->termination_status_ = diff --git a/chromium/content/browser/child_process_launcher.h b/chromium/content/browser/child_process_launcher.h index 5a6e1f96e26..7ce780551f4 100644 --- a/chromium/content/browser/child_process_launcher.h +++ b/chromium/content/browser/child_process_launcher.h @@ -41,7 +41,7 @@ class CONTENT_EXPORT ChildProcessLauncher { SandboxedProcessLauncherDelegate* delegate, #elif defined(OS_POSIX) bool use_zygote, - const base::EnvironmentVector& environ, + const base::EnvironmentMap& environ, int ipcfd, #endif CommandLine* cmd_line, @@ -58,6 +58,11 @@ class CONTENT_EXPORT ChildProcessLauncher { // Call this when the child process exits to know what happened to it. // |known_dead| can be true if we already know the process is dead as it can // help the implemention figure the proper TerminationStatus. + // On Linux, the use of |known_dead| is subtle and can be crucial if an + // accurate status is important. With |known_dead| set to false, a dead + // process could be seen as running. With |known_dead| set to true, the + // process will be killed if it was still running. See ZygoteHostImpl for + // more discussion of Linux implementation details. // |exit_code| is the exit code of the process if it exited (e.g. status from // waitpid if on posix, from GetExitCodeProcess on Windows). |exit_code| may // be NULL. diff --git a/chromium/content/browser/child_process_security_policy_browsertest.cc b/chromium/content/browser/child_process_security_policy_browsertest.cc index db27a533ea9..c7cfe39c167 100644 --- a/chromium/content/browser/child_process_security_policy_browsertest.cc +++ b/chromium/content/browser/child_process_security_policy_browsertest.cc @@ -10,7 +10,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/result_codes.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/content/browser/child_process_security_policy_impl.cc b/chromium/content/browser/child_process_security_policy_impl.cc index aefbf71fa62..6933f9e0b1d 100644 --- a/chromium/content/browser/child_process_security_policy_impl.cc +++ b/chromium/content/browser/child_process_security_policy_impl.cc @@ -11,7 +11,9 @@ #include "base/platform_file.h" #include "base/stl_util.h" #include "base/strings/string_util.h" +#include "content/browser/plugin_process_host.h" #include "content/browser/site_instance_impl.h" +#include "content/public/browser/child_process_data.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/bindings_policy.h" @@ -51,7 +53,7 @@ const int kEnumerateDirectoryPermissions = base::PLATFORM_FILE_ENUMERATE; // TODO(tommycli): These flag sets need some work to make more obvious. -// Why for instance, does Create|Write != Create|Write? http://crbug.com/263150 +// Why for instance, does Create|Write != CreateWrite? http://crbug.com/263150 const int kCreateReadWriteFilePermissions = kReadFilePermissions | kWriteFilePermissions | @@ -75,7 +77,8 @@ class ChildProcessSecurityPolicyImpl::SecurityState { public: SecurityState() : enabled_bindings_(0), - can_read_raw_cookies_(false) { } + can_read_raw_cookies_(false), + can_send_midi_sysex_(false) { } ~SecurityState() { scheme_policy_.clear(); @@ -150,6 +153,10 @@ class ChildProcessSecurityPolicyImpl::SecurityState { can_read_raw_cookies_ = false; } + void GrantPermissionForMIDISysEx() { + can_send_midi_sysex_ = true; + } + // Determine whether permission has been granted to request |url|. bool CanRequestURL(const GURL& url) { // Having permission to a scheme implies permssion to all of its URLs. @@ -245,6 +252,10 @@ class ChildProcessSecurityPolicyImpl::SecurityState { return can_read_raw_cookies_; } + bool can_send_midi_sysex() const { + return can_send_midi_sysex_; + } + private: typedef std::map<std::string, bool> SchemeMap; @@ -270,6 +281,8 @@ class ChildProcessSecurityPolicyImpl::SecurityState { bool can_read_raw_cookies_; + bool can_send_midi_sysex_; + GURL origin_lock_; // The set of isolated filesystems the child process is permitted to access. @@ -280,8 +293,8 @@ class ChildProcessSecurityPolicyImpl::SecurityState { ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { // We know about these schemes and believe them to be safe. - RegisterWebSafeScheme(chrome::kHttpScheme); - RegisterWebSafeScheme(chrome::kHttpsScheme); + RegisterWebSafeScheme(kHttpScheme); + RegisterWebSafeScheme(kHttpsScheme); RegisterWebSafeScheme(chrome::kFtpScheme); RegisterWebSafeScheme(chrome::kDataScheme); RegisterWebSafeScheme("feed"); @@ -290,7 +303,7 @@ ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { // We know about the following pseudo schemes and treat them specially. RegisterPseudoScheme(chrome::kAboutScheme); - RegisterPseudoScheme(chrome::kJavaScriptScheme); + RegisterPseudoScheme(kJavaScriptScheme); RegisterPseudoScheme(kViewSourceScheme); } @@ -487,6 +500,16 @@ void ChildProcessSecurityPolicyImpl::GrantCopyIntoFileSystem( kCreateFilePermissions); } +void ChildProcessSecurityPolicyImpl::GrantSendMIDISysExMessage(int child_id) { + base::AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(child_id); + if (state == security_state_.end()) + return; + + state->second->GrantPermissionForMIDISysEx(); +} + void ChildProcessSecurityPolicyImpl::GrantScheme(int child_id, const std::string& scheme) { base::AutoLock lock(lock_); @@ -781,6 +804,17 @@ bool ChildProcessSecurityPolicyImpl::CanAccessCookiesForOrigin( bool ChildProcessSecurityPolicyImpl::CanSendCookiesForOrigin(int child_id, const GURL& gurl) { + for (PluginProcessHostIterator iter; !iter.Done(); ++iter) { + if (iter.GetData().process_type == child_id) { + if (iter.GetData().process_type == PROCESS_TYPE_PLUGIN) { + // NPAPI plugin processes are unsandboxed and so are trusted. Plugins + // can make request to any origin. + return true; + } + break; + } + } + base::AutoLock lock(lock_); SecurityStateMap::iterator state = security_state_.find(child_id); if (state == security_state_.end()) @@ -829,4 +863,14 @@ void ChildProcessSecurityPolicyImpl::RegisterFileSystemPermissionPolicy( file_system_policy_map_[type] = policy; } +bool ChildProcessSecurityPolicyImpl::CanSendMIDISysExMessage(int child_id) { + base::AutoLock lock(lock_); + + SecurityStateMap::iterator state = security_state_.find(child_id); + if (state == security_state_.end()) + return false; + + return state->second->can_send_midi_sysex(); +} + } // namespace content diff --git a/chromium/content/browser/child_process_security_policy_impl.h b/chromium/content/browser/child_process_security_policy_impl.h index 3477f1edb1a..0a874b3beb4 100644 --- a/chromium/content/browser/child_process_security_policy_impl.h +++ b/chromium/content/browser/child_process_security_policy_impl.h @@ -122,6 +122,9 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl // Revoke read raw cookies permission. void RevokeReadRawCookies(int child_id); + // Grants permission to send system exclusive message to any MIDI devices. + void GrantSendMIDISysExMessage(int child_id); + // Before servicing a child process's request for a URL, the browser should // call this method to determine whether the process has the capability to // request the URL. @@ -138,20 +141,6 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl // the browser should call this method to check for the capability. bool CanReadDirectory(int child_id, const base::FilePath& directory); - // Deprecated: Use CanReadFile, etc. methods instead. - // Determines if certain permissions were granted for a file. |permissions| - // must be a bitwise-or'd value of base::PlatformFileFlags. - bool HasPermissionsForFile(int child_id, - const base::FilePath& file, - int permissions); - - // Deprecated: Use CanReadFileSystemFile, etc. methods instead. - // Determines if certain permissions were granted for a file in FileSystem - // API. |permissions| must be a bitwise-or'd value of base::PlatformFileFlags. - bool HasPermissionsForFileSystemFile(int child_id, - const fileapi::FileSystemURL& url, - int permissions); - // Explicit permissions checks for FileSystemURL specified files. bool CanReadFileSystemFile(int child_id, const fileapi::FileSystemURL& url); bool CanWriteFileSystemFile(int child_id, const fileapi::FileSystemURL& url); @@ -200,11 +189,15 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl fileapi::FileSystemType type, int policy); + // Returns true if sending system exclusive messages is allowed. + bool CanSendMIDISysExMessage(int child_id); + private: friend class ChildProcessSecurityPolicyInProcessBrowserTest; friend class ChildProcessSecurityPolicyTest; FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyInProcessBrowserTest, NoLeak); + FRIEND_TEST_ALL_PREFIXES(ChildProcessSecurityPolicyTest, FilePermissions); class SecurityState; @@ -241,6 +234,20 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl const std::string& filesystem_id, int permission); + // Deprecated: Use CanReadFile, etc. methods instead. + // Determines if certain permissions were granted for a file. |permissions| + // must be a bitwise-or'd value of base::PlatformFileFlags. + bool HasPermissionsForFile(int child_id, + const base::FilePath& file, + int permissions); + + // Deprecated: Use CanReadFileSystemFile, etc. methods instead. + // Determines if certain permissions were granted for a file in FileSystem + // API. |permissions| must be a bitwise-or'd value of base::PlatformFileFlags. + bool HasPermissionsForFileSystemFile(int child_id, + const fileapi::FileSystemURL& url, + int permissions); + // You must acquire this lock before reading or writing any members of this // class. You must not block while holding this lock. base::Lock lock_; diff --git a/chromium/content/browser/child_process_security_policy_unittest.cc b/chromium/content/browser/child_process_security_policy_unittest.cc index b914eac549a..247a74f69d5 100644 --- a/chromium/content/browser/child_process_security_policy_unittest.cc +++ b/chromium/content/browser/child_process_security_policy_unittest.cc @@ -99,8 +99,8 @@ TEST_F(ChildProcessSecurityPolicyTest, IsWebSafeSchemeTest) { ChildProcessSecurityPolicyImpl* p = ChildProcessSecurityPolicyImpl::GetInstance(); - EXPECT_TRUE(p->IsWebSafeScheme(chrome::kHttpScheme)); - EXPECT_TRUE(p->IsWebSafeScheme(chrome::kHttpsScheme)); + EXPECT_TRUE(p->IsWebSafeScheme(kHttpScheme)); + EXPECT_TRUE(p->IsWebSafeScheme(kHttpsScheme)); EXPECT_TRUE(p->IsWebSafeScheme(chrome::kFtpScheme)); EXPECT_TRUE(p->IsWebSafeScheme(chrome::kDataScheme)); EXPECT_TRUE(p->IsWebSafeScheme("feed")); @@ -119,7 +119,7 @@ TEST_F(ChildProcessSecurityPolicyTest, IsPseudoSchemeTest) { ChildProcessSecurityPolicyImpl::GetInstance(); EXPECT_TRUE(p->IsPseudoScheme(chrome::kAboutScheme)); - EXPECT_TRUE(p->IsPseudoScheme(chrome::kJavaScriptScheme)); + EXPECT_TRUE(p->IsPseudoScheme(kJavaScriptScheme)); EXPECT_TRUE(p->IsPseudoScheme(kViewSourceScheme)); EXPECT_FALSE(p->IsPseudoScheme("registered-pseudo-scheme")); diff --git a/chromium/content/browser/database_browsertest.cc b/chromium/content/browser/database_browsertest.cc index 305d8ff94c0..373a06f2a70 100644 --- a/chromium/content/browser/database_browsertest.cc +++ b/chromium/content/browser/database_browsertest.cc @@ -11,7 +11,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "content/test/net/url_request_mock_http_job.h" diff --git a/chromium/content/browser/device_orientation/data_fetcher_impl_android.cc b/chromium/content/browser/device_orientation/data_fetcher_impl_android.cc index 6748d744b71..8d4919c4d58 100644 --- a/chromium/content/browser/device_orientation/data_fetcher_impl_android.cc +++ b/chromium/content/browser/device_orientation/data_fetcher_impl_android.cc @@ -26,7 +26,9 @@ const int kPeriodInMilliseconds = 100; DataFetcherImplAndroid::DataFetcherImplAndroid() : number_active_device_motion_sensors_(0), device_motion_buffer_(NULL), - is_buffer_ready_(false) { + device_orientation_buffer_(NULL), + is_motion_buffer_ready_(false), + is_orientation_buffer_ready_(false) { memset(received_motion_data_, 0, sizeof(received_motion_data_)); device_orientation_.Reset( Java_DeviceMotionAndOrientation_getInstance(AttachCurrentThread())); @@ -66,18 +68,41 @@ const Orientation* DataFetcherImplAndroid::GetOrientation() { void DataFetcherImplAndroid::GotOrientation( JNIEnv*, jobject, double alpha, double beta, double gamma) { - base::AutoLock autolock(next_orientation_lock_); + { + // TODO(timvolodine): remove this part once Device Orientation is + // completely implemented using shared memory. + base::AutoLock autolock(next_orientation_lock_); + + Orientation* orientation = new Orientation(); + orientation->set_alpha(alpha); + orientation->set_beta(beta); + orientation->set_gamma(gamma); + orientation->set_absolute(true); + next_orientation_ = orientation; + } - Orientation* orientation = new Orientation(); - orientation->set_alpha(alpha); - orientation->set_beta(beta); - orientation->set_gamma(gamma); - orientation->set_absolute(true); - next_orientation_ = orientation; + if (device_orientation_buffer_) { + device_orientation_buffer_->seqlock.WriteBegin(); + device_orientation_buffer_->data.alpha = alpha; + device_orientation_buffer_->data.hasAlpha = true; + device_orientation_buffer_->data.beta = beta; + device_orientation_buffer_->data.hasBeta = true; + device_orientation_buffer_->data.gamma = gamma; + device_orientation_buffer_->data.hasGamma = true; + device_orientation_buffer_->seqlock.WriteEnd(); + + if (!is_orientation_buffer_ready_) + SetOrientationBufferReadyStatus(true); + } } void DataFetcherImplAndroid::GotAcceleration( JNIEnv*, jobject, double x, double y, double z) { + base::AutoLock autolock(motion_buffer_lock_); + + if (!device_motion_buffer_) + return; + device_motion_buffer_->seqlock.WriteBegin(); device_motion_buffer_->data.accelerationX = x; device_motion_buffer_->data.hasAccelerationX = true; @@ -87,14 +112,19 @@ void DataFetcherImplAndroid::GotAcceleration( device_motion_buffer_->data.hasAccelerationZ = true; device_motion_buffer_->seqlock.WriteEnd(); - if (!is_buffer_ready_) { + if (!is_motion_buffer_ready_) { received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] = 1; - CheckBufferReadyToRead(); + CheckMotionBufferReadyToRead(); } } void DataFetcherImplAndroid::GotAccelerationIncludingGravity( JNIEnv*, jobject, double x, double y, double z) { + base::AutoLock autolock(motion_buffer_lock_); + + if (!device_motion_buffer_) + return; + device_motion_buffer_->seqlock.WriteBegin(); device_motion_buffer_->data.accelerationIncludingGravityX = x; device_motion_buffer_->data.hasAccelerationIncludingGravityX = true; @@ -104,14 +134,19 @@ void DataFetcherImplAndroid::GotAccelerationIncludingGravity( device_motion_buffer_->data.hasAccelerationIncludingGravityZ = true; device_motion_buffer_->seqlock.WriteEnd(); - if (!is_buffer_ready_) { + if (!is_motion_buffer_ready_) { received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY] = 1; - CheckBufferReadyToRead(); + CheckMotionBufferReadyToRead(); } } void DataFetcherImplAndroid::GotRotationRate( JNIEnv*, jobject, double alpha, double beta, double gamma) { + base::AutoLock autolock(motion_buffer_lock_); + + if (!device_motion_buffer_) + return; + device_motion_buffer_->seqlock.WriteBegin(); device_motion_buffer_->data.rotationRateAlpha = alpha; device_motion_buffer_->data.hasRotationRateAlpha = true; @@ -121,9 +156,9 @@ void DataFetcherImplAndroid::GotRotationRate( device_motion_buffer_->data.hasRotationRateGamma = true; device_motion_buffer_->seqlock.WriteEnd(); - if (!is_buffer_ready_) { + if (!is_motion_buffer_ready_) { received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] = 1; - CheckBufferReadyToRead(); + CheckMotionBufferReadyToRead(); } } @@ -151,26 +186,41 @@ int DataFetcherImplAndroid::GetNumberActiveDeviceMotionSensors() { // ----- Shared memory API methods +// --- Device Motion + bool DataFetcherImplAndroid::StartFetchingDeviceMotionData( DeviceMotionHardwareBuffer* buffer) { - device_motion_buffer_ = buffer; - ClearInternalBuffers(); + DCHECK(buffer); + { + base::AutoLock autolock(motion_buffer_lock_); + device_motion_buffer_ = buffer; + ClearInternalMotionBuffers(); + } bool success = Start(DeviceData::kTypeMotion); // If no motion data can ever be provided, the number of active device motion // sensors will be zero. In that case flag the shared memory buffer // as ready to read, as it will not change anyway. number_active_device_motion_sensors_ = GetNumberActiveDeviceMotionSensors(); - CheckBufferReadyToRead(); + { + base::AutoLock autolock(motion_buffer_lock_); + CheckMotionBufferReadyToRead(); + } return success; } void DataFetcherImplAndroid::StopFetchingDeviceMotionData() { Stop(DeviceData::kTypeMotion); - ClearInternalBuffers(); + { + base::AutoLock autolock(motion_buffer_lock_); + if (device_motion_buffer_) { + ClearInternalMotionBuffers(); + device_motion_buffer_ = NULL; + } + } } -void DataFetcherImplAndroid::CheckBufferReadyToRead() { +void DataFetcherImplAndroid::CheckMotionBufferReadyToRead() { if (received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] + received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY] + received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] == @@ -178,21 +228,50 @@ void DataFetcherImplAndroid::CheckBufferReadyToRead() { device_motion_buffer_->seqlock.WriteBegin(); device_motion_buffer_->data.interval = kPeriodInMilliseconds; device_motion_buffer_->seqlock.WriteEnd(); - SetBufferReadyStatus(true); + SetMotionBufferReadyStatus(true); } } -void DataFetcherImplAndroid::SetBufferReadyStatus(bool ready) { +void DataFetcherImplAndroid::SetMotionBufferReadyStatus(bool ready) { device_motion_buffer_->seqlock.WriteBegin(); device_motion_buffer_->data.allAvailableSensorsAreActive = ready; device_motion_buffer_->seqlock.WriteEnd(); - is_buffer_ready_ = ready; + is_motion_buffer_ready_ = ready; } -void DataFetcherImplAndroid::ClearInternalBuffers() { +void DataFetcherImplAndroid::ClearInternalMotionBuffers() { memset(received_motion_data_, 0, sizeof(received_motion_data_)); number_active_device_motion_sensors_ = 0; - SetBufferReadyStatus(false); + SetMotionBufferReadyStatus(false); +} + +// --- Device Orientation + +void DataFetcherImplAndroid::SetOrientationBufferReadyStatus(bool ready) { + device_orientation_buffer_->seqlock.WriteBegin(); + device_orientation_buffer_->data.allAvailableSensorsAreActive = ready; + device_orientation_buffer_->seqlock.WriteEnd(); + is_orientation_buffer_ready_ = ready; +} + +bool DataFetcherImplAndroid::StartFetchingDeviceOrientationData( + DeviceOrientationHardwareBuffer* buffer) { + DCHECK(buffer); + device_orientation_buffer_ = buffer; + bool success = Start(DeviceData::kTypeOrientation); + + // If Start() was unsuccessful then set the buffer ready flag to true + // to start firing all-null events. + SetOrientationBufferReadyStatus(!success); + return success; +} + +void DataFetcherImplAndroid::StopFetchingDeviceOrientationData() { + Stop(DeviceData::kTypeOrientation); + if (device_orientation_buffer_) { + SetOrientationBufferReadyStatus(false); + device_orientation_buffer_ = NULL; + } } } // namespace content diff --git a/chromium/content/browser/device_orientation/data_fetcher_impl_android.h b/chromium/content/browser/device_orientation/data_fetcher_impl_android.h index 9926e7d1464..f348e1d3e54 100644 --- a/chromium/content/browser/device_orientation/data_fetcher_impl_android.h +++ b/chromium/content/browser/device_orientation/data_fetcher_impl_android.h @@ -11,6 +11,7 @@ #include "content/browser/device_orientation/device_data.h" #include "content/common/content_export.h" #include "content/common/device_motion_hardware_buffer.h" +#include "content/common/device_orientation/device_orientation_hardware_buffer.h" template<typename T> struct DefaultSingletonTraits; @@ -55,6 +56,10 @@ class CONTENT_EXPORT DataFetcherImplAndroid { bool StartFetchingDeviceMotionData(DeviceMotionHardwareBuffer* buffer); void StopFetchingDeviceMotionData(); + bool StartFetchingDeviceOrientationData( + DeviceOrientationHardwareBuffer* buffer); + void StopFetchingDeviceOrientationData(); + protected: DataFetcherImplAndroid(); virtual ~DataFetcherImplAndroid(); @@ -66,9 +71,11 @@ class CONTENT_EXPORT DataFetcherImplAndroid { const Orientation* GetOrientation(); - void CheckBufferReadyToRead(); - void SetBufferReadyStatus(bool ready); - void ClearInternalBuffers(); + void CheckMotionBufferReadyToRead(); + void SetMotionBufferReadyStatus(bool ready); + void ClearInternalMotionBuffers(); + + void SetOrientationBufferReadyStatus(bool ready); enum { RECEIVED_MOTION_DATA_ACCELERATION = 0, @@ -88,7 +95,11 @@ class CONTENT_EXPORT DataFetcherImplAndroid { int number_active_device_motion_sensors_; int received_motion_data_[RECEIVED_MOTION_DATA_MAX]; DeviceMotionHardwareBuffer* device_motion_buffer_; - bool is_buffer_ready_; + DeviceOrientationHardwareBuffer* device_orientation_buffer_; + bool is_motion_buffer_ready_; + bool is_orientation_buffer_ready_; + + base::Lock motion_buffer_lock_; DISALLOW_COPY_AND_ASSIGN(DataFetcherImplAndroid); }; diff --git a/chromium/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc b/chromium/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc index 85d5184689e..d12c8c6feb1 100644 --- a/chromium/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc +++ b/chromium/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc @@ -42,10 +42,12 @@ class FakeDataFetcherImplAndroid : public DataFetcherImplAndroid { class AndroidDataFetcherTest : public testing::Test { protected: AndroidDataFetcherTest() { - buffer_.reset(new DeviceMotionHardwareBuffer); + motion_buffer_.reset(new DeviceMotionHardwareBuffer); + orientation_buffer_.reset(new DeviceOrientationHardwareBuffer); } - scoped_ptr<DeviceMotionHardwareBuffer> buffer_; + scoped_ptr<DeviceMotionHardwareBuffer> motion_buffer_; + scoped_ptr<DeviceOrientationHardwareBuffer> orientation_buffer_; }; TEST_F(AndroidDataFetcherTest, ThreeDeviceMotionSensorsActive) { @@ -53,21 +55,39 @@ TEST_F(AndroidDataFetcherTest, ThreeDeviceMotionSensorsActive) { FakeDataFetcherImplAndroid fetcher; fetcher.SetNumberActiveDeviceMotionSensors(3); - fetcher.StartFetchingDeviceMotionData(buffer_.get()); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); + fetcher.StartFetchingDeviceMotionData(motion_buffer_.get()); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); fetcher.GotAcceleration(0, 0, 1, 2, 3); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); - - fetcher.GotAccelerationIncludingGravity(0, 0, 1, 2, 3); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); - - fetcher.GotRotationRate(0, 0, 1, 2, 3); - ASSERT_TRUE(buffer_->data.allAvailableSensorsAreActive); - ASSERT_EQ(kPeriodInMilliseconds, buffer_->data.interval); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); + ASSERT_EQ(1, motion_buffer_->data.accelerationX); + ASSERT_TRUE(motion_buffer_->data.hasAccelerationX); + ASSERT_EQ(2, motion_buffer_->data.accelerationY); + ASSERT_TRUE(motion_buffer_->data.hasAccelerationY); + ASSERT_EQ(3, motion_buffer_->data.accelerationZ); + ASSERT_TRUE(motion_buffer_->data.hasAccelerationZ); + + fetcher.GotAccelerationIncludingGravity(0, 0, 4, 5, 6); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); + ASSERT_EQ(4, motion_buffer_->data.accelerationIncludingGravityX); + ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityX); + ASSERT_EQ(5, motion_buffer_->data.accelerationIncludingGravityY); + ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityY); + ASSERT_EQ(6, motion_buffer_->data.accelerationIncludingGravityZ); + ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityZ); + + fetcher.GotRotationRate(0, 0, 7, 8, 9); + ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive); + ASSERT_EQ(7, motion_buffer_->data.rotationRateAlpha); + ASSERT_TRUE(motion_buffer_->data.hasRotationRateAlpha); + ASSERT_EQ(8, motion_buffer_->data.rotationRateBeta); + ASSERT_TRUE(motion_buffer_->data.hasRotationRateBeta); + ASSERT_EQ(9, motion_buffer_->data.rotationRateGamma); + ASSERT_TRUE(motion_buffer_->data.hasRotationRateGamma); + ASSERT_EQ(kPeriodInMilliseconds, motion_buffer_->data.interval); fetcher.StopFetchingDeviceMotionData(); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); } TEST_F(AndroidDataFetcherTest, TwoDeviceMotionSensorsActive) { @@ -75,18 +95,18 @@ TEST_F(AndroidDataFetcherTest, TwoDeviceMotionSensorsActive) { FakeDataFetcherImplAndroid fetcher; fetcher.SetNumberActiveDeviceMotionSensors(2); - fetcher.StartFetchingDeviceMotionData(buffer_.get()); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); + fetcher.StartFetchingDeviceMotionData(motion_buffer_.get()); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); fetcher.GotAcceleration(0, 0, 1, 2, 3); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); fetcher.GotAccelerationIncludingGravity(0, 0, 1, 2, 3); - ASSERT_TRUE(buffer_->data.allAvailableSensorsAreActive); - ASSERT_EQ(kPeriodInMilliseconds, buffer_->data.interval); + ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive); + ASSERT_EQ(kPeriodInMilliseconds, motion_buffer_->data.interval); fetcher.StopFetchingDeviceMotionData(); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); } TEST_F(AndroidDataFetcherTest, ZeroDeviceMotionSensorsActive) { @@ -94,14 +114,35 @@ TEST_F(AndroidDataFetcherTest, ZeroDeviceMotionSensorsActive) { FakeDataFetcherImplAndroid fetcher; fetcher.SetNumberActiveDeviceMotionSensors(0); - fetcher.StartFetchingDeviceMotionData(buffer_.get()); - ASSERT_TRUE(buffer_->data.allAvailableSensorsAreActive); - ASSERT_EQ(kPeriodInMilliseconds, buffer_->data.interval); + fetcher.StartFetchingDeviceMotionData(motion_buffer_.get()); + ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive); + ASSERT_EQ(kPeriodInMilliseconds, motion_buffer_->data.interval); fetcher.StopFetchingDeviceMotionData(); - ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive); + ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive); } +TEST_F(AndroidDataFetcherTest, DeviceOrientationSensorsActive) { + FakeDataFetcherImplAndroid::Register(base::android::AttachCurrentThread()); + FakeDataFetcherImplAndroid fetcher; + + fetcher.StartFetchingDeviceOrientationData(orientation_buffer_.get()); + ASSERT_FALSE(orientation_buffer_->data.allAvailableSensorsAreActive); + + fetcher.GotOrientation(0, 0, 1, 2, 3); + ASSERT_TRUE(orientation_buffer_->data.allAvailableSensorsAreActive); + ASSERT_EQ(1, orientation_buffer_->data.alpha); + ASSERT_TRUE(orientation_buffer_->data.hasAlpha); + ASSERT_EQ(2, orientation_buffer_->data.beta); + ASSERT_TRUE(orientation_buffer_->data.hasBeta); + ASSERT_EQ(3, orientation_buffer_->data.gamma); + ASSERT_TRUE(orientation_buffer_->data.hasGamma); + + fetcher.StopFetchingDeviceOrientationData(); + ASSERT_FALSE(orientation_buffer_->data.allAvailableSensorsAreActive); +} + + } // namespace } // namespace content diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory.h b/chromium/content/browser/device_orientation/data_fetcher_shared_memory.h index dc5e92ae160..dad1cb60dd2 100644 --- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory.h +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory.h @@ -5,48 +5,59 @@ #ifndef CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_H_ #define CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_H_ -#include "content/browser/device_orientation/device_data.h" +#include "content/browser/device_orientation/data_fetcher_shared_memory_base.h" + +#if !defined(OS_ANDROID) #include "content/common/device_motion_hardware_buffer.h" +#include "content/common/device_orientation/device_orientation_hardware_buffer.h" +#endif -namespace WebKit { -class WebDeviceMotionData; -} +#if defined(OS_MACOSX) +class SuddenMotionSensor; +#elif defined(OS_WIN) +#include <SensorsApi.h> +#include "base/win/scoped_comptr.h" +#endif namespace content { -class CONTENT_EXPORT DataFetcherSharedMemory { +class CONTENT_EXPORT DataFetcherSharedMemory + : public DataFetcherSharedMemoryBase { + public: - DataFetcherSharedMemory() - : device_motion_buffer_(NULL), - started_(false) { } + DataFetcherSharedMemory(); virtual ~DataFetcherSharedMemory(); - // Returns true if this fetcher needs explicit calls to fetch the data. - // Called from any thread. - virtual bool NeedsPolling(); - - // If this fetcher NeedsPolling() is true, this method will update the - // buffer with the latest device motion data. - // Returns true if there was any motion data to update the buffer with. - // Called from the DeviceMotionProvider::PollingThread. - virtual bool FetchDeviceMotionDataIntoBuffer(); - - // Returns true if the relevant sensors could be successfully activated. - // This method should be called before any calls to - // FetchDeviceMotionDataIntoBuffer(). - // If NeedsPolling() is true this method should be called from the - // PollingThread. - virtual bool StartFetchingDeviceMotionData( - DeviceMotionHardwareBuffer* buffer); - - // Indicates to the fetcher to stop fetching device data. - // If NeedsPolling() is true this method should be called from the - // PollingThread. - virtual void StopFetchingDeviceMotionData(); - private: - DeviceMotionHardwareBuffer* device_motion_buffer_; - bool started_; + virtual bool Start(ConsumerType consumer_type, void* buffer) OVERRIDE; + virtual bool Stop(ConsumerType consumer_type) OVERRIDE; + +#if !defined(OS_ANDROID) + DeviceMotionHardwareBuffer* motion_buffer_; + DeviceOrientationHardwareBuffer* orientation_buffer_; +#endif +#if defined(OS_MACOSX) + virtual void Fetch(unsigned consumer_bitmask) OVERRIDE; + virtual bool IsPolling() const OVERRIDE; + + scoped_ptr<SuddenMotionSensor> sudden_motion_sensor_; +#elif defined(OS_WIN) + class SensorEventSink; + class SensorEventSinkMotion; + class SensorEventSinkOrientation; + + virtual bool IsPolling() const OVERRIDE; + virtual base::TimeDelta GetPollDelay() const OVERRIDE; + + bool RegisterForSensor(REFSENSOR_TYPE_ID sensor_type, ISensor** sensor, + scoped_refptr<SensorEventSink> event_sink); + void DisableSensors(ConsumerType consumer_type); + void SetBufferAvailableState(ConsumerType consumer_type, bool enabled); + + base::win::ScopedComPtr<ISensor> sensor_inclinometer_; + base::win::ScopedComPtr<ISensor> sensor_accelerometer_; + base::win::ScopedComPtr<ISensor> sensor_gyrometer_; +#endif DISALLOW_COPY_AND_ASSIGN(DataFetcherSharedMemory); }; diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_android.cc b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_android.cc index 8f6e202c042..3f00c3d179f 100644 --- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_android.cc +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_android.cc @@ -2,39 +2,52 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "data_fetcher_shared_memory.h" +#include "content/browser/device_orientation/data_fetcher_shared_memory.h" #include "base/logging.h" -#include "data_fetcher_impl_android.h" +#include "content/browser/device_orientation/data_fetcher_impl_android.h" +#include "content/common/device_motion_hardware_buffer.h" +#include "content/common/device_orientation/device_orientation_hardware_buffer.h" namespace content { -DataFetcherSharedMemory::~DataFetcherSharedMemory() { - if (started_) - StopFetchingDeviceMotionData(); +DataFetcherSharedMemory::DataFetcherSharedMemory() { } -bool DataFetcherSharedMemory::NeedsPolling() { - return false; -} - -bool DataFetcherSharedMemory::FetchDeviceMotionDataIntoBuffer() { - NOTREACHED(); - return false; +DataFetcherSharedMemory::~DataFetcherSharedMemory() { } -bool DataFetcherSharedMemory::StartFetchingDeviceMotionData( - DeviceMotionHardwareBuffer* buffer) { +bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) { DCHECK(buffer); - device_motion_buffer_ = buffer; - started_ = DataFetcherImplAndroid::GetInstance()-> - StartFetchingDeviceMotionData(buffer); - return started_; + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + return DataFetcherImplAndroid::GetInstance()-> + StartFetchingDeviceMotionData( + static_cast<DeviceMotionHardwareBuffer*>(buffer)); + case CONSUMER_TYPE_ORIENTATION: + return DataFetcherImplAndroid::GetInstance()-> + StartFetchingDeviceOrientationData( + static_cast<DeviceOrientationHardwareBuffer*>(buffer)); + default: + NOTREACHED(); + } + return false; } -void DataFetcherSharedMemory::StopFetchingDeviceMotionData() { - DataFetcherImplAndroid::GetInstance()->StopFetchingDeviceMotionData(); - started_ = false; +bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) { + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + DataFetcherImplAndroid::GetInstance()->StopFetchingDeviceMotionData(); + return true; + case CONSUMER_TYPE_ORIENTATION: + DataFetcherImplAndroid::GetInstance()-> + StopFetchingDeviceOrientationData(); + return true; + default: + NOTREACHED(); + } + return false; } } // namespace content diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.cc b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.cc new file mode 100644 index 00000000000..429713c781f --- /dev/null +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.cc @@ -0,0 +1,247 @@ +// Copyright 2013 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/browser/device_orientation/data_fetcher_shared_memory_base.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/threading/thread.h" +#include "base/timer/timer.h" +#include "content/common/device_motion_hardware_buffer.h" +#include "content/common/device_orientation/device_orientation_hardware_buffer.h" + +namespace content { + +namespace { +const int kPeriodInMilliseconds = 100; + +static size_t GetConsumerSharedMemoryBufferSize(ConsumerType consumer_type) { + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + return sizeof(DeviceMotionHardwareBuffer); + case CONSUMER_TYPE_ORIENTATION: + return sizeof(DeviceOrientationHardwareBuffer); + default: + NOTREACHED(); + } + return 0; +} + +} + +class DataFetcherSharedMemoryBase::PollingThread : public base::Thread { + public: + PollingThread(const char* name, DataFetcherSharedMemoryBase* fetcher); + virtual ~PollingThread(); + + void AddConsumer(ConsumerType consumer_type, void* buffer); + void RemoveConsumer(ConsumerType consumer_type); + + unsigned GetConsumersBitmask() const { return consumers_bitmask_; } + bool IsTimerRunning() const { return timer_ ? timer_->IsRunning() : false; } + + private: + + void DoPoll(); + + unsigned consumers_bitmask_; + DataFetcherSharedMemoryBase* fetcher_; + base::TimeDelta poll_interval_; + scoped_ptr<base::RepeatingTimer<PollingThread> > timer_; + + DISALLOW_COPY_AND_ASSIGN(PollingThread); +}; + +// --- PollingThread methods + +DataFetcherSharedMemoryBase::PollingThread::PollingThread( + const char* name, DataFetcherSharedMemoryBase* fetcher) + : base::Thread(name), + consumers_bitmask_(0), + fetcher_(fetcher), + poll_interval_(fetcher->GetPollDelay()) { +} + +DataFetcherSharedMemoryBase::PollingThread::~PollingThread() { +} + +void DataFetcherSharedMemoryBase::PollingThread::AddConsumer( + ConsumerType consumer_type, void* buffer) { + DCHECK(fetcher_); + if (!fetcher_->Start(consumer_type, buffer)) + return; + + consumers_bitmask_ |= consumer_type; + + if (!timer_ && poll_interval_.InMilliseconds() > 0) { + timer_.reset(new base::RepeatingTimer<PollingThread>()); + timer_->Start(FROM_HERE, + poll_interval_, + this, &PollingThread::DoPoll); + } +} + +void DataFetcherSharedMemoryBase::PollingThread::RemoveConsumer( + ConsumerType consumer_type) { + DCHECK(fetcher_); + if (!fetcher_->Stop(consumer_type)) + return; + + consumers_bitmask_ ^= consumer_type; + + if (!consumers_bitmask_) + timer_.reset(); // will also stop the timer. +} + +void DataFetcherSharedMemoryBase::PollingThread::DoPoll() { + DCHECK(fetcher_); + DCHECK(consumers_bitmask_); + fetcher_->Fetch(consumers_bitmask_); +} + +// --- end of PollingThread methods + +DataFetcherSharedMemoryBase::DataFetcherSharedMemoryBase() + : started_consumers_(0) { +} + +DataFetcherSharedMemoryBase::~DataFetcherSharedMemoryBase() { + StopFetchingDeviceData(CONSUMER_TYPE_MOTION); + StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); + + // make sure polling thread stops asap. + if (polling_thread_) + polling_thread_->Stop(); + + STLDeleteContainerPairSecondPointers(shared_memory_map_.begin(), + shared_memory_map_.end()); +} + +bool DataFetcherSharedMemoryBase::StartFetchingDeviceData( + ConsumerType consumer_type) { + if (started_consumers_ & consumer_type) + return true; + + void* buffer = GetSharedMemoryBuffer(consumer_type); + if (!buffer) + return false; + + if (IsPolling()) { + if (!InitAndStartPollingThreadIfNecessary()) + return false; + polling_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&PollingThread::AddConsumer, + base::Unretained(polling_thread_.get()), + consumer_type, buffer)); + } else { + if (!Start(consumer_type, buffer)) + return false; + } + + started_consumers_ |= consumer_type; + + return true; +} + +bool DataFetcherSharedMemoryBase::StopFetchingDeviceData( + ConsumerType consumer_type) { + if (!(started_consumers_ & consumer_type)) + return true; + + if (IsPolling()) { + polling_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&PollingThread::RemoveConsumer, + base::Unretained(polling_thread_.get()), + consumer_type)); + } else { + if (!Stop(consumer_type)) + return false; + } + + started_consumers_ ^= consumer_type; + + return true; +} + +base::SharedMemoryHandle +DataFetcherSharedMemoryBase::GetSharedMemoryHandleForProcess( + ConsumerType consumer_type, base::ProcessHandle process) { + SharedMemoryMap::const_iterator it = shared_memory_map_.find(consumer_type); + if (it == shared_memory_map_.end()) + return base::SharedMemory::NULLHandle(); + + base::SharedMemoryHandle renderer_handle; + it->second->ShareToProcess(process, &renderer_handle); + return renderer_handle; +} + +bool DataFetcherSharedMemoryBase::InitAndStartPollingThreadIfNecessary() { + if (polling_thread_) + return true; + + polling_thread_.reset( + new PollingThread("Inertial Device Sensor poller", this)); + + if (!polling_thread_->Start()) { + LOG(ERROR) << "Failed to start inertial sensor data polling thread"; + return false; + } + return true; +} + +void DataFetcherSharedMemoryBase::Fetch(unsigned consumer_bitmask) { + NOTIMPLEMENTED(); +} + +bool DataFetcherSharedMemoryBase::IsPolling() const { + return false; +} + +base::TimeDelta DataFetcherSharedMemoryBase::GetPollDelay() const { + return base::TimeDelta::FromMilliseconds(kPeriodInMilliseconds); +} + +base::SharedMemory* DataFetcherSharedMemoryBase::GetSharedMemory( + ConsumerType consumer_type) { + SharedMemoryMap::const_iterator it = shared_memory_map_.find(consumer_type); + if (it != shared_memory_map_.end()) + return it->second; + + size_t buffer_size = GetConsumerSharedMemoryBufferSize(consumer_type); + if (buffer_size == 0) + return NULL; + + scoped_ptr<base::SharedMemory> new_shared_mem(new base::SharedMemory); + if (new_shared_mem->CreateAndMapAnonymous(buffer_size)) { + if (void* mem = new_shared_mem->memory()) { + memset(mem, 0, buffer_size); + base::SharedMemory* shared_mem = new_shared_mem.release(); + shared_memory_map_[consumer_type] = shared_mem; + return shared_mem; + } + } + LOG(ERROR) << "Failed to initialize shared memory"; + return NULL; +} + +void* DataFetcherSharedMemoryBase::GetSharedMemoryBuffer( + ConsumerType consumer_type) { + if (base::SharedMemory* shared_memory = GetSharedMemory(consumer_type)) + return shared_memory->memory(); + return NULL; +} + +base::MessageLoop* DataFetcherSharedMemoryBase::GetPollingMessageLoop() const { + return polling_thread_ ? polling_thread_->message_loop() : NULL; +} + +bool DataFetcherSharedMemoryBase::IsPollingTimerRunningForTesting() const { + return polling_thread_ ? polling_thread_->IsTimerRunning() : false; +} + + +} // namespace content diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.h b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.h new file mode 100644 index 00000000000..8894b014a01 --- /dev/null +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base.h @@ -0,0 +1,90 @@ +// Copyright 2013 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_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_BASE_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_BASE_H_ + +#include <map> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop.h" +#include "content/browser/device_orientation/inertial_sensor_consts.h" +#include "content/common/content_export.h" + +namespace content { + +// Sensor data fetchers should derive from this base class and implement +// the abstract Start() and Stop() methods. +// If the fetcher requires polling it should also implement IsPolling() +// to return true and the Fetch() method which will be called from the +// polling thread to fetch data at regular intervals. +class CONTENT_EXPORT DataFetcherSharedMemoryBase { + public: + + // Starts updating the shared memory buffer with sensor data at + // regular intervals. Returns true if the relevant sensors could + // be successfully activated. + bool StartFetchingDeviceData(ConsumerType consumer_type); + + // Stops updating the shared memory buffer. Returns true if the + // relevant sensors could be successfully deactivated. + bool StopFetchingDeviceData(ConsumerType consumer_type); + + // Returns the shared memory handle of the device sensor data + // duplicated into the given process. This method should only be + // called after a call to StartFetchingDeviceData method with + // corresponding |consumer_type| parameter. + base::SharedMemoryHandle GetSharedMemoryHandleForProcess( + ConsumerType consumer_type, base::ProcessHandle process); + + protected: + class PollingThread; + + DataFetcherSharedMemoryBase(); + virtual ~DataFetcherSharedMemoryBase(); + + // Returns the message loop of the polling thread. + // Returns NULL if there is no polling thread. + base::MessageLoop* GetPollingMessageLoop() const; + + // If IsPolling() is true this method is called from the |polling_thread_| + // at regular intervals. + virtual void Fetch(unsigned consumer_bitmask); + + // Returns true if this fetcher needs to use a polling thread to + // fetch the sensor data. + virtual bool IsPolling() const; + + // Returns the interval between successive calls to Fetch(). + // If interval is zero, Fetch() is never called. + virtual base::TimeDelta GetPollDelay() const; + + // Start() method should call InitSharedMemoryBuffer() to get the shared + // memory pointer. If IsPolling() is true both Start() and Stop() methods + // are called from the |polling_thread_|. + virtual bool Start(ConsumerType consumer_type, void* buffer) = 0; + virtual bool Stop(ConsumerType consumer_type) = 0; + + bool IsPollingTimerRunningForTesting() const; + + private: + bool InitAndStartPollingThreadIfNecessary(); + base::SharedMemory* GetSharedMemory(ConsumerType consumer_type); + void* GetSharedMemoryBuffer(ConsumerType consumer_type); + + unsigned started_consumers_; + + scoped_ptr<PollingThread> polling_thread_; + + // Owning pointers. Objects in the map are deleted in dtor. + typedef std::map<ConsumerType, base::SharedMemory*> SharedMemoryMap; + SharedMemoryMap shared_memory_map_; + + DISALLOW_COPY_AND_ASSIGN(DataFetcherSharedMemoryBase); +}; + +} + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_SHARED_MEMORY_BASE_H_ diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc new file mode 100644 index 00000000000..f2028a6c436 --- /dev/null +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc @@ -0,0 +1,407 @@ +// Copyright 2013 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/browser/device_orientation/data_fetcher_shared_memory_base.h" + +#include "base/logging.h" +#include "base/process/process_handle.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "content/common/device_motion_hardware_buffer.h" +#include "content/common/device_orientation/device_orientation_hardware_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +namespace { + +const int kPeriodInMilliseconds = 100; + + +class FakeDataFetcher : public DataFetcherSharedMemoryBase { + public: + FakeDataFetcher() + : start_motion_(false, false), + start_orientation_(false, false), + stop_motion_(false, false), + stop_orientation_(false, false), + updated_motion_(false, false), + updated_orientation_(false, false), + motion_buffer_(NULL), + orientation_buffer_(NULL) { + } + virtual ~FakeDataFetcher() { } + + bool Init(ConsumerType consumer_type, void* buffer) { + EXPECT_TRUE(buffer); + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer); + break; + case CONSUMER_TYPE_ORIENTATION: + orientation_buffer_ = + static_cast<DeviceOrientationHardwareBuffer*>(buffer); + break; + default: + return false; + } + return true; + } + + void UpdateMotion() { + DeviceMotionHardwareBuffer* buffer = GetMotionBuffer(); + ASSERT_TRUE(buffer); + buffer->seqlock.WriteBegin(); + buffer->data.interval = kPeriodInMilliseconds; + buffer->seqlock.WriteEnd(); + updated_motion_.Signal(); + } + + void UpdateOrientation() { + DeviceOrientationHardwareBuffer* buffer = GetOrientationBuffer(); + ASSERT_TRUE(buffer); + buffer->seqlock.WriteBegin(); + buffer->data.alpha = 1; + buffer->seqlock.WriteEnd(); + updated_orientation_.Signal(); + } + + DeviceMotionHardwareBuffer* GetMotionBuffer() const { + return motion_buffer_; + } + + DeviceOrientationHardwareBuffer* GetOrientationBuffer() const { + return orientation_buffer_; + } + + void WaitForStart(ConsumerType consumer_type) { + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + start_motion_.Wait(); + break; + case CONSUMER_TYPE_ORIENTATION: + start_orientation_.Wait(); + break; + } + } + + void WaitForStop(ConsumerType consumer_type) { + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + stop_motion_.Wait(); + break; + case CONSUMER_TYPE_ORIENTATION: + stop_orientation_.Wait(); + break; + } + } + + void WaitForUpdate(ConsumerType consumer_type) { + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + updated_motion_.Wait(); + break; + case CONSUMER_TYPE_ORIENTATION: + updated_orientation_.Wait(); + break; + } + } + + protected: + base::WaitableEvent start_motion_; + base::WaitableEvent start_orientation_; + base::WaitableEvent stop_motion_; + base::WaitableEvent stop_orientation_; + base::WaitableEvent updated_motion_; + base::WaitableEvent updated_orientation_; + + private: + DeviceMotionHardwareBuffer* motion_buffer_; + DeviceOrientationHardwareBuffer* orientation_buffer_; + + DISALLOW_COPY_AND_ASSIGN(FakeDataFetcher); +}; + +class FakeNonPollingDataFetcher : public FakeDataFetcher { + public: + FakeNonPollingDataFetcher() { } + virtual ~FakeNonPollingDataFetcher() { } + + virtual bool Start(ConsumerType consumer_type, void* buffer) OVERRIDE { + Init(consumer_type, buffer); + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + UpdateMotion(); + start_motion_.Signal(); + break; + case CONSUMER_TYPE_ORIENTATION: + UpdateOrientation(); + start_orientation_.Signal(); + break; + default: + return false; + } + return true; + } + + virtual bool Stop(ConsumerType consumer_type) OVERRIDE { + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + stop_motion_.Signal(); + break; + case CONSUMER_TYPE_ORIENTATION: + stop_orientation_.Signal(); + break; + default: + return false; + } + return true; + } + + virtual bool IsPolling() const OVERRIDE { + return false; + } + + virtual void Fetch(unsigned consumer_bitmask) OVERRIDE { + FAIL() << "fetch should not be called, " + << "because this is a non-polling fetcher"; + } + + private: + + DISALLOW_COPY_AND_ASSIGN(FakeNonPollingDataFetcher); +}; + +class FakePollingDataFetcher : public FakeDataFetcher { + public: + FakePollingDataFetcher() { } + virtual ~FakePollingDataFetcher() { } + + virtual bool Start(ConsumerType consumer_type, void* buffer) OVERRIDE { + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); + + Init(consumer_type, buffer); + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + start_motion_.Signal(); + break; + case CONSUMER_TYPE_ORIENTATION: + start_orientation_.Signal(); + break; + default: + return false; + } + return true; + } + + virtual bool Stop(ConsumerType consumer_type) OVERRIDE { + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + stop_motion_.Signal(); + break; + case CONSUMER_TYPE_ORIENTATION: + stop_orientation_.Signal(); + break; + default: + return false; + } + return true; + } + + virtual void Fetch(unsigned consumer_bitmask) OVERRIDE { + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); + EXPECT_TRUE(consumer_bitmask & CONSUMER_TYPE_ORIENTATION || + consumer_bitmask & CONSUMER_TYPE_MOTION); + + if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION) + UpdateOrientation(); + if (consumer_bitmask & CONSUMER_TYPE_MOTION) + UpdateMotion(); + } + + virtual bool IsPolling() const OVERRIDE { + return true; + } + + private: + + DISALLOW_COPY_AND_ASSIGN(FakePollingDataFetcher); +}; + +class FakeZeroDelayPollingDataFetcher : public FakeDataFetcher { + public: + FakeZeroDelayPollingDataFetcher() { } + virtual ~FakeZeroDelayPollingDataFetcher() { } + + virtual bool Start(ConsumerType consumer_type, void* buffer) OVERRIDE { + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); + + Init(consumer_type, buffer); + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + start_motion_.Signal(); + break; + case CONSUMER_TYPE_ORIENTATION: + start_orientation_.Signal(); + break; + default: + return false; + } + return true; + } + + virtual bool Stop(ConsumerType consumer_type) OVERRIDE { + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + stop_motion_.Signal(); + break; + case CONSUMER_TYPE_ORIENTATION: + stop_orientation_.Signal(); + break; + default: + return false; + } + return true; + } + + virtual void Fetch(unsigned consumer_bitmask) OVERRIDE { + FAIL() << "fetch should not be called"; + } + + virtual bool IsPolling() const OVERRIDE { + return true; + } + + virtual base::TimeDelta GetPollDelay() const OVERRIDE { + return base::TimeDelta::FromMilliseconds(0); + } + + bool IsPollingTimerRunningForTesting() const { + return FakeDataFetcher::IsPollingTimerRunningForTesting(); + } + + private: + + DISALLOW_COPY_AND_ASSIGN(FakeZeroDelayPollingDataFetcher); +}; + + +TEST(DataFetcherSharedMemoryBaseTest, DoesStartMotion) { + FakeNonPollingDataFetcher fake_data_fetcher; + EXPECT_FALSE(fake_data_fetcher.IsPolling()); + + EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION)); + fake_data_fetcher.WaitForStart(CONSUMER_TYPE_MOTION); + + EXPECT_EQ(kPeriodInMilliseconds, + fake_data_fetcher.GetMotionBuffer()->data.interval); + + fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION); + fake_data_fetcher.WaitForStop(CONSUMER_TYPE_MOTION); +} + +TEST(DataFetcherSharedMemoryBaseTest, DoesStartOrientation) { + FakeNonPollingDataFetcher fake_data_fetcher; + EXPECT_FALSE(fake_data_fetcher.IsPolling()); + + EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData( + CONSUMER_TYPE_ORIENTATION)); + fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION); + + EXPECT_EQ(1, fake_data_fetcher.GetOrientationBuffer()->data.alpha); + + fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION); +} + +TEST(DataFetcherSharedMemoryBaseTest, DoesPollMotion) { + FakePollingDataFetcher fake_data_fetcher; + EXPECT_TRUE(fake_data_fetcher.IsPolling()); + + EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION)); + fake_data_fetcher.WaitForStart(CONSUMER_TYPE_MOTION); + fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_MOTION); + + EXPECT_EQ(kPeriodInMilliseconds, + fake_data_fetcher.GetMotionBuffer()->data.interval); + + fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION); + fake_data_fetcher.WaitForStop(CONSUMER_TYPE_MOTION); +} + +TEST(DataFetcherSharedMemoryBaseTest, DoesPollOrientation) { + FakePollingDataFetcher fake_data_fetcher; + EXPECT_TRUE(fake_data_fetcher.IsPolling()); + + EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData( + CONSUMER_TYPE_ORIENTATION)); + fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_ORIENTATION); + + EXPECT_EQ(1, fake_data_fetcher.GetOrientationBuffer()->data.alpha); + + fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION); +} + +TEST(DataFetcherSharedMemoryBaseTest, DoesPollMotionAndOrientation) { + FakePollingDataFetcher fake_data_fetcher; + EXPECT_TRUE(fake_data_fetcher.IsPolling()); + + EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData( + CONSUMER_TYPE_ORIENTATION)); + base::SharedMemoryHandle handle_orientation = + fake_data_fetcher.GetSharedMemoryHandleForProcess( + CONSUMER_TYPE_ORIENTATION, base::GetCurrentProcessHandle()); + EXPECT_TRUE(base::SharedMemory::IsHandleValid(handle_orientation)); + + EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData( + CONSUMER_TYPE_MOTION)); + base::SharedMemoryHandle handle_motion = + fake_data_fetcher.GetSharedMemoryHandleForProcess( + CONSUMER_TYPE_MOTION, base::GetCurrentProcessHandle()); + EXPECT_TRUE(base::SharedMemory::IsHandleValid(handle_motion)); + + fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.WaitForStart(CONSUMER_TYPE_MOTION); + + fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_MOTION); + + EXPECT_EQ(1, fake_data_fetcher.GetOrientationBuffer()->data.alpha); + EXPECT_EQ(kPeriodInMilliseconds, + fake_data_fetcher.GetMotionBuffer()->data.interval); + + fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION); + fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.WaitForStop(CONSUMER_TYPE_MOTION); +} + +TEST(DataFetcherSharedMemoryBaseTest, DoesNotPollZeroDelay) { + FakeZeroDelayPollingDataFetcher fake_data_fetcher; + EXPECT_TRUE(fake_data_fetcher.IsPolling()); + EXPECT_EQ(0, fake_data_fetcher.GetPollDelay().InMilliseconds()); + + EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData( + CONSUMER_TYPE_ORIENTATION)); + fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION); + + EXPECT_FALSE(fake_data_fetcher.IsPollingTimerRunningForTesting()); + EXPECT_EQ(0, fake_data_fetcher.GetOrientationBuffer()->data.alpha); + + fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); + fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION); +} + + + +} // namespace + +} // namespace content diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_default.cc b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_default.cc index 0f66fc79773..c05cdcdbb8f 100644 --- a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_default.cc +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_default.cc @@ -6,38 +6,67 @@ #include "base/logging.h" -namespace content { +namespace { -DataFetcherSharedMemory::~DataFetcherSharedMemory() { - if (started_) - StopFetchingDeviceMotionData(); +static bool SetMotionBuffer(content::DeviceMotionHardwareBuffer* buffer, + bool enabled) { + if (!buffer) + return false; + buffer->seqlock.WriteBegin(); + buffer->data.allAvailableSensorsAreActive = enabled; + buffer->seqlock.WriteEnd(); + return true; } -bool DataFetcherSharedMemory::NeedsPolling() { - return false; +static bool SetOrientationBuffer( + content::DeviceOrientationHardwareBuffer* buffer, bool enabled) { + if (!buffer) + return false; + buffer->seqlock.WriteBegin(); + buffer->data.allAvailableSensorsAreActive = enabled; + buffer->seqlock.WriteEnd(); + return true; } -bool DataFetcherSharedMemory::FetchDeviceMotionDataIntoBuffer() { - NOTREACHED(); - return false; } -bool DataFetcherSharedMemory::StartFetchingDeviceMotionData( - DeviceMotionHardwareBuffer* buffer) { +namespace content { + +DataFetcherSharedMemory::DataFetcherSharedMemory() + : motion_buffer_(NULL), orientation_buffer_(NULL) { +} + +DataFetcherSharedMemory::~DataFetcherSharedMemory() { +} + +bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) { DCHECK(buffer); - device_motion_buffer_ = buffer; - device_motion_buffer_->seqlock.WriteBegin(); - device_motion_buffer_->data.allAvailableSensorsAreActive = true; - device_motion_buffer_->seqlock.WriteEnd(); - started_ = true; - return true; + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer); + return SetMotionBuffer(motion_buffer_, true); + case CONSUMER_TYPE_ORIENTATION: + orientation_buffer_ = + static_cast<DeviceOrientationHardwareBuffer*>(buffer); + return SetOrientationBuffer(orientation_buffer_, true); + default: + NOTREACHED(); + } + return false; } -void DataFetcherSharedMemory::StopFetchingDeviceMotionData() { - device_motion_buffer_->seqlock.WriteBegin(); - device_motion_buffer_->data.allAvailableSensorsAreActive = false; - device_motion_buffer_->seqlock.WriteEnd(); - started_ = false; +bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) { + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + return SetMotionBuffer(motion_buffer_, false); + case CONSUMER_TYPE_ORIENTATION: + return SetOrientationBuffer(orientation_buffer_, false); + default: + NOTREACHED(); + } + return false; } } // namespace content diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc new file mode 100644 index 00000000000..f606bda3a61 --- /dev/null +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc @@ -0,0 +1,166 @@ +// Copyright 2013 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 "data_fetcher_shared_memory.h" + +#include "base/logging.h" +#include "third_party/sudden_motion_sensor/sudden_motion_sensor_mac.h" + +namespace { + +const double kMeanGravity = 9.80665; + +void FetchMotion(SuddenMotionSensor* sensor, + content::DeviceMotionHardwareBuffer* buffer) { + DCHECK(buffer); + + float axis_value[3]; + if (!sensor->ReadSensorValues(axis_value)) + return; + + buffer->seqlock.WriteBegin(); + buffer->data.accelerationIncludingGravityX = axis_value[0] * kMeanGravity; + buffer->data.hasAccelerationIncludingGravityX = true; + buffer->data.accelerationIncludingGravityY = axis_value[1] * kMeanGravity; + buffer->data.hasAccelerationIncludingGravityY = true; + buffer->data.accelerationIncludingGravityZ = axis_value[2] * kMeanGravity; + buffer->data.hasAccelerationIncludingGravityZ = true; + buffer->data.allAvailableSensorsAreActive = true; + buffer->seqlock.WriteEnd(); +} + +void FetchOrientation(SuddenMotionSensor* sensor, + content::DeviceOrientationHardwareBuffer* buffer) { + DCHECK(buffer); + + // Retrieve per-axis calibrated values. + float axis_value[3]; + if (!sensor->ReadSensorValues(axis_value)) + return; + + // Transform the accelerometer values to W3C draft angles. + // + // Accelerometer values are just dot products of the sensor axes + // by the gravity vector 'g' with the result for the z axis inverted. + // + // To understand this transformation calculate the 3rd row of the z-x-y + // Euler angles rotation matrix (because of the 'g' vector, only 3rd row + // affects to the result). Note that z-x-y matrix means R = Ry * Rx * Rz. + // Then, assume alpha = 0 and you get this: + // + // x_acc = sin(gamma) + // y_acc = - cos(gamma) * sin(beta) + // z_acc = cos(beta) * cos(gamma) + // + // After that the rest is just a bit of trigonometry. + // + // Also note that alpha can't be provided but it's assumed to be always zero. + // This is necessary in order to provide enough information to solve + // the equations. + // + const double kRad2deg = 180.0 / M_PI; + double beta = kRad2deg * atan2(-axis_value[1], axis_value[2]); + double gamma = kRad2deg * asin(axis_value[0]); + + // TODO(aousterh): should absolute_ be set to false here? + // See crbug.com/136010. + + // Make sure that the interval boundaries comply with the specification. At + // this point, beta is [-180, 180] and gamma is [-90, 90], but the spec has + // the upper bound open on both. + if (beta == 180.0) + beta = -180; // -180 == 180 (upside-down) + if (gamma == 90.0) + gamma = nextafter(90, 0); + + // At this point, DCHECKing is paranoia. Never hurts. + DCHECK_GE(beta, -180.0); + DCHECK_LT(beta, 180.0); + DCHECK_GE(gamma, -90.0); + DCHECK_LT(gamma, 90.0); + + buffer->seqlock.WriteBegin(); + buffer->data.beta = beta; + buffer->data.hasBeta = true; + buffer->data.gamma = gamma; + buffer->data.hasGamma = true; + buffer->data.allAvailableSensorsAreActive = true; + buffer->seqlock.WriteEnd(); +} + +} // namespace + +namespace content { + +DataFetcherSharedMemory::DataFetcherSharedMemory() { +} + +DataFetcherSharedMemory::~DataFetcherSharedMemory() { +} + +void DataFetcherSharedMemory::Fetch(unsigned consumer_bitmask) { + DCHECK(base::MessageLoop::current() == GetPollingMessageLoop()); + DCHECK(sudden_motion_sensor_); + DCHECK(consumer_bitmask & CONSUMER_TYPE_ORIENTATION || + consumer_bitmask & CONSUMER_TYPE_MOTION); + + if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION) + FetchOrientation(sudden_motion_sensor_.get(), orientation_buffer_); + if (consumer_bitmask & CONSUMER_TYPE_MOTION) + FetchMotion(sudden_motion_sensor_.get(), motion_buffer_); +} + +bool DataFetcherSharedMemory::IsPolling() const { + return true; +} + +bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) { + DCHECK(base::MessageLoop::current() == GetPollingMessageLoop()); + DCHECK(buffer); + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer); + if (!sudden_motion_sensor_) + sudden_motion_sensor_.reset(SuddenMotionSensor::Create()); + return sudden_motion_sensor_.get() != NULL; + case CONSUMER_TYPE_ORIENTATION: + orientation_buffer_ = + static_cast<DeviceOrientationHardwareBuffer*>(buffer); + if (!sudden_motion_sensor_) + sudden_motion_sensor_.reset(SuddenMotionSensor::Create()); + return sudden_motion_sensor_.get() != NULL; + default: + NOTREACHED(); + } + return false; +} + +bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) { + DCHECK(base::MessageLoop::current() == GetPollingMessageLoop()); + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + if (motion_buffer_) { + motion_buffer_->seqlock.WriteBegin(); + motion_buffer_->data.allAvailableSensorsAreActive = false; + motion_buffer_->seqlock.WriteEnd(); + motion_buffer_ = NULL; + } + return true; + case CONSUMER_TYPE_ORIENTATION: + if (orientation_buffer_) { + orientation_buffer_->seqlock.WriteBegin(); + orientation_buffer_->data.allAvailableSensorsAreActive = false; + orientation_buffer_->seqlock.WriteEnd(); + orientation_buffer_ = NULL; + } + return true; + default: + NOTREACHED(); + } + return false; +} + +} // namespace content diff --git a/chromium/content/browser/device_orientation/data_fetcher_shared_memory_win.cc b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_win.cc new file mode 100644 index 00000000000..65389890ee2 --- /dev/null +++ b/chromium/content/browser/device_orientation/data_fetcher_shared_memory_win.cc @@ -0,0 +1,394 @@ +// Copyright 2013 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 "data_fetcher_shared_memory.h" + +#include <GuidDef.h> +#include <InitGuid.h> +#include <PortableDeviceTypes.h> +#include <Sensors.h> + +#include "base/logging.h" +#include "base/win/iunknown_impl.h" +#include "base/win/windows_version.h" + +namespace { + +const double kMeanGravity = 9.80665; +const int kPeriodInMilliseconds = 100; + +} // namespace + + +namespace content { + +class DataFetcherSharedMemory::SensorEventSink + : public ISensorEvents, public base::win::IUnknownImpl { + public: + SensorEventSink() {} + virtual ~SensorEventSink() {} + + // IUnknown interface + virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE { + return IUnknownImpl::AddRef(); + } + + virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE { + return IUnknownImpl::Release(); + } + + virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE { + if (riid == __uuidof(ISensorEvents)) { + *ppv = static_cast<ISensorEvents*>(this); + AddRef(); + return S_OK; + } + return IUnknownImpl::QueryInterface(riid, ppv); + } + + // ISensorEvents interface + STDMETHODIMP OnEvent(ISensor* sensor, + REFGUID event_id, + IPortableDeviceValues* event_data) OVERRIDE { + return S_OK; + } + + STDMETHODIMP OnLeave(REFSENSOR_ID sensor_id) OVERRIDE { + return S_OK; + } + + STDMETHODIMP OnStateChanged(ISensor* sensor, SensorState state) OVERRIDE { + return S_OK; + } + + STDMETHODIMP OnDataUpdated(ISensor* sensor, + ISensorDataReport* new_data) OVERRIDE { + if (NULL == new_data || NULL == sensor) + return E_INVALIDARG; + return UpdateSharedMemoryBuffer(sensor, new_data) ? S_OK : E_FAIL; + } + +protected: + virtual bool UpdateSharedMemoryBuffer( + ISensor* sensor, ISensorDataReport* new_data) = 0; + + void GetSensorValue(REFPROPERTYKEY property, ISensorDataReport* new_data, + double* value, bool* has_value) { + PROPVARIANT variant_value = {}; + if (SUCCEEDED(new_data->GetSensorValue(property, &variant_value))) { + if (variant_value.vt == VT_R8) + *value = variant_value.dblVal; + else if (variant_value.vt == VT_R4) + *value = variant_value.fltVal; + *has_value = true; + } else { + *value = 0; + *has_value = false; + } + } + + private: + + DISALLOW_COPY_AND_ASSIGN(SensorEventSink); +}; + +class DataFetcherSharedMemory::SensorEventSinkOrientation + : public DataFetcherSharedMemory::SensorEventSink { + public: + explicit SensorEventSinkOrientation( + DeviceOrientationHardwareBuffer* const buffer) : buffer_(buffer) {} + virtual ~SensorEventSinkOrientation() {} + +protected: + virtual bool UpdateSharedMemoryBuffer( + ISensor* sensor, ISensorDataReport* new_data) OVERRIDE { + double alpha, beta, gamma; + bool has_alpha, has_beta, has_gamma; + + GetSensorValue(SENSOR_DATA_TYPE_TILT_X_DEGREES, new_data, &alpha, + &has_alpha); + GetSensorValue(SENSOR_DATA_TYPE_TILT_Y_DEGREES, new_data, &beta, + &has_beta); + GetSensorValue(SENSOR_DATA_TYPE_TILT_Z_DEGREES, new_data, &gamma, + &has_gamma); + + if (buffer_) { + buffer_->seqlock.WriteBegin(); + buffer_->data.alpha = alpha; + buffer_->data.hasAlpha = has_alpha; + buffer_->data.beta = beta; + buffer_->data.hasBeta = has_beta; + buffer_->data.gamma = gamma; + buffer_->data.hasGamma = has_gamma; + buffer_->data.absolute = true; + buffer_->data.hasAbsolute = has_alpha || has_beta || has_gamma; + buffer_->data.allAvailableSensorsAreActive = true; + buffer_->seqlock.WriteEnd(); + } + + return true; + } + + private: + DeviceOrientationHardwareBuffer* const buffer_; + + DISALLOW_COPY_AND_ASSIGN(SensorEventSinkOrientation); +}; + +class DataFetcherSharedMemory::SensorEventSinkMotion + : public DataFetcherSharedMemory::SensorEventSink { + public: + explicit SensorEventSinkMotion(DeviceMotionHardwareBuffer* const buffer) + : buffer_(buffer) {} + virtual ~SensorEventSinkMotion() {} + + protected: + virtual bool UpdateSharedMemoryBuffer( + ISensor* sensor, ISensorDataReport* new_data) OVERRIDE { + + SENSOR_TYPE_ID sensor_type = GUID_NULL; + if (!SUCCEEDED(sensor->GetType(&sensor_type))) + return false; + + if (IsEqualIID(sensor_type, SENSOR_TYPE_ACCELEROMETER_3D)) { + double acceleration_including_gravity_x; + double acceleration_including_gravity_y; + double acceleration_including_gravity_z; + bool has_acceleration_including_gravity_x; + bool has_acceleration_including_gravity_y; + bool has_acceleration_including_gravity_z; + + GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_X_G, new_data, + &acceleration_including_gravity_x, + &has_acceleration_including_gravity_x); + GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Y_G, new_data, + &acceleration_including_gravity_y, + &has_acceleration_including_gravity_y); + GetSensorValue(SENSOR_DATA_TYPE_ACCELERATION_Z_G, new_data, + &acceleration_including_gravity_z, + &has_acceleration_including_gravity_z); + + if (buffer_) { + buffer_->seqlock.WriteBegin(); + buffer_->data.accelerationIncludingGravityX = + -acceleration_including_gravity_x * kMeanGravity; + buffer_->data.hasAccelerationIncludingGravityX = + has_acceleration_including_gravity_x; + buffer_->data.accelerationIncludingGravityY = + -acceleration_including_gravity_y * kMeanGravity; + buffer_->data.hasAccelerationIncludingGravityY = + has_acceleration_including_gravity_y; + buffer_->data.accelerationIncludingGravityZ = + -acceleration_including_gravity_z * kMeanGravity; + buffer_->data.hasAccelerationIncludingGravityZ = + has_acceleration_including_gravity_z; + // TODO(timvolodine): consider setting the two variables below + // after all sensors have fired. + buffer_->data.interval = kPeriodInMilliseconds; + buffer_->data.allAvailableSensorsAreActive = true; + buffer_->seqlock.WriteEnd(); + } + + } else if (IsEqualIID(sensor_type, SENSOR_TYPE_GYROMETER_3D)) { + double alpha, beta, gamma; + bool has_alpha, has_beta, has_gamma; + + GetSensorValue(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, + new_data, &alpha, &has_alpha); + GetSensorValue(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, + new_data, &beta, &has_beta); + GetSensorValue(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND, + new_data, &gamma, &has_gamma); + + if (buffer_) { + buffer_->seqlock.WriteBegin(); + buffer_->data.rotationRateAlpha = alpha; + buffer_->data.hasRotationRateAlpha = has_alpha; + buffer_->data.rotationRateBeta = beta; + buffer_->data.hasRotationRateBeta = has_beta; + buffer_->data.rotationRateGamma = gamma; + buffer_->data.hasRotationRateGamma = has_gamma; + buffer_->data.interval = kPeriodInMilliseconds; + buffer_->data.allAvailableSensorsAreActive = true; + buffer_->seqlock.WriteEnd(); + } + } + + return true; + } + + private: + DeviceMotionHardwareBuffer* const buffer_; + + DISALLOW_COPY_AND_ASSIGN(SensorEventSinkMotion); + }; + + +DataFetcherSharedMemory::DataFetcherSharedMemory() + : motion_buffer_(NULL), + orientation_buffer_(NULL) { +} + +DataFetcherSharedMemory::~DataFetcherSharedMemory() { +} + +bool DataFetcherSharedMemory::IsPolling() const { + return true; +} + +base::TimeDelta DataFetcherSharedMemory::GetPollDelay() const { + // We only need a new thread for this fetcher, the actual interface + // is push-bashed so no need for explicit callbacks to Fetch(). + return base::TimeDelta::FromMilliseconds(0); +} + +bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) { + DCHECK(buffer); + + switch (consumer_type) { + case CONSUMER_TYPE_ORIENTATION: + { + orientation_buffer_ = + static_cast<DeviceOrientationHardwareBuffer*>(buffer); + scoped_refptr<SensorEventSink> sink( + new SensorEventSinkOrientation(orientation_buffer_)); + if (RegisterForSensor(SENSOR_TYPE_INCLINOMETER_3D, + sensor_inclinometer_.Receive(), sink)) + return true; + // if no sensors are available set buffer to ready, to fire null-events. + SetBufferAvailableState(consumer_type, true); + } + break; + case CONSUMER_TYPE_MOTION: + { + motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer); + scoped_refptr<SensorEventSink> sink( + new SensorEventSinkMotion(motion_buffer_)); + bool accelerometer_available = RegisterForSensor( + SENSOR_TYPE_ACCELEROMETER_3D, sensor_accelerometer_.Receive(), + sink); + bool gyrometer_available = RegisterForSensor( + SENSOR_TYPE_GYROMETER_3D, sensor_gyrometer_.Receive(), sink); + if (accelerometer_available || gyrometer_available) + return true; + // if no sensors are available set buffer to ready, to fire null-events. + SetBufferAvailableState(consumer_type, true); + } + break; + default: + NOTREACHED(); + } + return false; +} + +bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) { + DisableSensors(consumer_type); + SetBufferAvailableState(consumer_type, false); + switch (consumer_type) { + case CONSUMER_TYPE_ORIENTATION: + orientation_buffer_ = NULL; + return true; + case CONSUMER_TYPE_MOTION: + motion_buffer_ = NULL; + return true; + default: + NOTREACHED(); + } + return false; +} + +bool DataFetcherSharedMemory::RegisterForSensor( + REFSENSOR_TYPE_ID sensor_type, + ISensor** sensor, + scoped_refptr<SensorEventSink> event_sink) { + if (base::win::GetVersion() < base::win::VERSION_WIN7) + return false; + + base::win::ScopedComPtr<ISensorManager> sensor_manager; + HRESULT hr = sensor_manager.CreateInstance(CLSID_SensorManager); + if (FAILED(hr) || !sensor_manager) + return false; + + base::win::ScopedComPtr<ISensorCollection> sensor_collection; + hr = sensor_manager->GetSensorsByType( + sensor_type, sensor_collection.Receive()); + + if (FAILED(hr) || !sensor_collection) + return false; + + ULONG count = 0; + hr = sensor_collection->GetCount(&count); + if (FAILED(hr) || !count) + return false; + + hr = sensor_collection->GetAt(0, sensor); + if (FAILED(hr) || !(*sensor)) + return false; + + base::win::ScopedComPtr<IPortableDeviceValues> device_values; + if (SUCCEEDED(device_values.CreateInstance(CLSID_PortableDeviceValues))) { + if (SUCCEEDED(device_values->SetUnsignedIntegerValue( + SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, kPeriodInMilliseconds))) { + base::win::ScopedComPtr<IPortableDeviceValues> return_values; + (*sensor)->SetProperties(device_values.get(), return_values.Receive()); + } + } + + base::win::ScopedComPtr<ISensorEvents> sensor_events; + hr = event_sink->QueryInterface( + __uuidof(ISensorEvents), sensor_events.ReceiveVoid()); + if (FAILED(hr) || !sensor_events) + return false; + + hr = (*sensor)->SetEventSink(sensor_events); + if (FAILED(hr)) + return false; + + return true; +} + +void DataFetcherSharedMemory::DisableSensors(ConsumerType consumer_type) { + switch(consumer_type) { + case CONSUMER_TYPE_ORIENTATION: + if (sensor_inclinometer_) { + sensor_inclinometer_->SetEventSink(NULL); + sensor_inclinometer_.Release(); + } + break; + case CONSUMER_TYPE_MOTION: + if (sensor_accelerometer_) { + sensor_accelerometer_->SetEventSink(NULL); + sensor_accelerometer_.Release(); + } + if (sensor_gyrometer_) { + sensor_gyrometer_->SetEventSink(NULL); + sensor_gyrometer_.Release(); + } + break; + default: + NOTREACHED(); + } +} + +void DataFetcherSharedMemory::SetBufferAvailableState( + ConsumerType consumer_type, bool enabled) { + switch(consumer_type) { + case CONSUMER_TYPE_ORIENTATION: + if (orientation_buffer_) { + orientation_buffer_->seqlock.WriteBegin(); + orientation_buffer_->data.allAvailableSensorsAreActive = enabled; + orientation_buffer_->seqlock.WriteEnd(); + } + case CONSUMER_TYPE_MOTION: + if (motion_buffer_) { + motion_buffer_->seqlock.WriteBegin(); + motion_buffer_->data.allAvailableSensorsAreActive = enabled; + motion_buffer_->seqlock.WriteEnd(); + } + default: + NOTREACHED(); + } +} + +} // namespace content diff --git a/chromium/content/browser/device_orientation/device_inertial_sensor_service.cc b/chromium/content/browser/device_orientation/device_inertial_sensor_service.cc new file mode 100644 index 00000000000..ee90be16668 --- /dev/null +++ b/chromium/content/browser/device_orientation/device_inertial_sensor_service.cc @@ -0,0 +1,94 @@ +// Copyright 2013 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/browser/device_orientation/device_inertial_sensor_service.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "content/browser/device_orientation/data_fetcher_shared_memory.h" +#include "content/public/browser/render_process_host.h" + +namespace content { + +DeviceInertialSensorService::DeviceInertialSensorService() + : num_motion_readers_(0), + num_orientation_readers_(0), + is_shutdown_(false) { +} + +DeviceInertialSensorService::~DeviceInertialSensorService() { +} + +DeviceInertialSensorService* DeviceInertialSensorService::GetInstance() { + return Singleton<DeviceInertialSensorService, + LeakySingletonTraits<DeviceInertialSensorService> >::get(); +} + +void DeviceInertialSensorService::AddConsumer(ConsumerType consumer_type) { + if (!ChangeNumberConsumers(consumer_type, 1)) + return; + + DCHECK(GetNumberConsumers(consumer_type)); + + if (!data_fetcher_) + data_fetcher_.reset(new DataFetcherSharedMemory); + data_fetcher_->StartFetchingDeviceData(consumer_type); +} + +void DeviceInertialSensorService::RemoveConsumer(ConsumerType consumer_type) { + if (!ChangeNumberConsumers(consumer_type, -1)) + return; + + if (GetNumberConsumers(consumer_type) == 0) + data_fetcher_->StopFetchingDeviceData(consumer_type); +} + +bool DeviceInertialSensorService::ChangeNumberConsumers( + ConsumerType consumer_type, int delta) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (is_shutdown_) + return false; + + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + num_motion_readers_ += delta; + DCHECK(num_motion_readers_ >= 0); + return true; + case CONSUMER_TYPE_ORIENTATION: + num_orientation_readers_ += delta; + DCHECK(num_orientation_readers_ >= 0); + return true; + default: + NOTREACHED(); + } + return false; +} + +int DeviceInertialSensorService::GetNumberConsumers( + ConsumerType consumer_type) const { + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + return num_motion_readers_; + case CONSUMER_TYPE_ORIENTATION: + return num_orientation_readers_; + default: + NOTREACHED(); + } + return 0; +} + +base::SharedMemoryHandle +DeviceInertialSensorService::GetSharedMemoryHandleForProcess( + ConsumerType consumer_type, base::ProcessHandle handle) { + DCHECK(thread_checker_.CalledOnValidThread()); + return data_fetcher_->GetSharedMemoryHandleForProcess(consumer_type, handle); +} + +void DeviceInertialSensorService::Shutdown() { + data_fetcher_.reset(); + is_shutdown_ = true; +} + +} // namespace content diff --git a/chromium/content/browser/device_orientation/device_motion_service.h b/chromium/content/browser/device_orientation/device_inertial_sensor_service.h index 0720bb95968..028d15568ad 100644 --- a/chromium/content/browser/device_orientation/device_motion_service.h +++ b/chromium/content/browser/device_orientation/device_inertial_sensor_service.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_MOTION_SERVICE_H_ -#define CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_MOTION_SERVICE_H_ +#ifndef CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_INERTIAL_SENSOR_SERVICE_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_INERTIAL_SENSOR_SERVICE_H_ #include "base/basictypes.h" #include "base/callback_forward.h" @@ -11,55 +11,58 @@ #include "base/memory/shared_memory.h" #include "base/memory/singleton.h" #include "base/threading/thread_checker.h" +#include "content/browser/device_orientation/inertial_sensor_consts.h" #include "content/common/content_export.h" namespace content { class DataFetcherSharedMemory; -class DeviceMotionProvider; class RenderProcessHost; // Owns the DeviceMotionProvider (the background polling thread) and keeps // track of the number of consumers currently using the data (and pausing // the provider when not in use). -class CONTENT_EXPORT DeviceMotionService { +class CONTENT_EXPORT DeviceInertialSensorService { public: - // Returns the DeviceMotionService singleton. - static DeviceMotionService* GetInstance(); + // Returns the DeviceInertialSensorService singleton. + static DeviceInertialSensorService* GetInstance(); // Increments the number of users of the provider. The Provider is running // when there's > 0 users, and is paused when the count drops to 0. - // // Must be called on the I/O thread. - void AddConsumer(); + void AddConsumer(ConsumerType consumer_type); // Removes a consumer. Should be matched with an AddConsumer call. - // // Must be called on the I/O thread. - void RemoveConsumer(); + void RemoveConsumer(ConsumerType cosumer_type); // Returns the shared memory handle of the device motion data duplicated // into the given process. base::SharedMemoryHandle GetSharedMemoryHandleForProcess( - base::ProcessHandle handle); + ConsumerType consumer_type, base::ProcessHandle handle); - // Stop/join with the background thread in DeviceMotionProvider |provider_|. + // Stop/join with the background polling thread in |provider_|. void Shutdown(); private: - friend struct DefaultSingletonTraits<DeviceMotionService>; + friend struct DefaultSingletonTraits<DeviceInertialSensorService>; - DeviceMotionService(); - virtual ~DeviceMotionService(); + DeviceInertialSensorService(); + virtual ~DeviceInertialSensorService(); - int num_readers_; + bool ChangeNumberConsumers(ConsumerType consumer_type, + int delta); + int GetNumberConsumers(ConsumerType consumer_type) const; + + int num_motion_readers_; + int num_orientation_readers_; bool is_shutdown_; - scoped_ptr<DeviceMotionProvider> provider_; + scoped_ptr<DataFetcherSharedMemory> data_fetcher_; base::ThreadChecker thread_checker_; - DISALLOW_COPY_AND_ASSIGN(DeviceMotionService); + DISALLOW_COPY_AND_ASSIGN(DeviceInertialSensorService); }; } // namespace content -#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_MOTION_SERVICE_H_ +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_INERTIAL_SENSOR_SERVICE_H_ diff --git a/chromium/content/browser/device_orientation/device_motion_message_filter.cc b/chromium/content/browser/device_orientation/device_motion_message_filter.cc index 3c619c6cd16..bc33cac3912 100644 --- a/chromium/content/browser/device_orientation/device_motion_message_filter.cc +++ b/chromium/content/browser/device_orientation/device_motion_message_filter.cc @@ -4,7 +4,7 @@ #include "content/browser/device_orientation/device_motion_message_filter.h" -#include "content/browser/device_orientation/device_motion_service.h" +#include "content/browser/device_orientation/device_inertial_sensor_service.h" #include "content/common/device_orientation/device_motion_messages.h" namespace content { @@ -16,7 +16,8 @@ DeviceMotionMessageFilter::DeviceMotionMessageFilter() DeviceMotionMessageFilter::~DeviceMotionMessageFilter() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (is_started_) - DeviceMotionService::GetInstance()->RemoveConsumer(); + DeviceInertialSensorService::GetInstance()->RemoveConsumer( + CONSUMER_TYPE_MOTION); } bool DeviceMotionMessageFilter::OnMessageReceived( @@ -40,7 +41,8 @@ void DeviceMotionMessageFilter::OnDeviceMotionStartPolling() { if (is_started_) return; is_started_ = true; - DeviceMotionService::GetInstance()->AddConsumer(); + DeviceInertialSensorService::GetInstance()->AddConsumer( + CONSUMER_TYPE_MOTION); DidStartDeviceMotionPolling(); } @@ -49,13 +51,15 @@ void DeviceMotionMessageFilter::OnDeviceMotionStopPolling() { if (!is_started_) return; is_started_ = false; - DeviceMotionService::GetInstance()->RemoveConsumer(); + DeviceInertialSensorService::GetInstance()->RemoveConsumer( + CONSUMER_TYPE_MOTION); } void DeviceMotionMessageFilter::DidStartDeviceMotionPolling() { Send(new DeviceMotionMsg_DidStartPolling( - DeviceMotionService::GetInstance()->GetSharedMemoryHandleForProcess( - PeerHandle()))); + DeviceInertialSensorService::GetInstance()-> + GetSharedMemoryHandleForProcess( + CONSUMER_TYPE_MOTION, PeerHandle()))); } } // namespace content diff --git a/chromium/content/browser/device_orientation/device_motion_provider.cc b/chromium/content/browser/device_orientation/device_motion_provider.cc deleted file mode 100644 index e88d5af83f4..00000000000 --- a/chromium/content/browser/device_orientation/device_motion_provider.cc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2013 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/browser/device_orientation/device_motion_provider.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "base/threading/thread.h" -#include "base/timer/timer.h" -#include "content/browser/device_orientation/data_fetcher_shared_memory.h" -#include "content/common/device_motion_hardware_buffer.h" - -namespace content { - -namespace { -const int kPeriodInMilliseconds = 100; -} - -class DeviceMotionProvider::PollingThread : public base::Thread { - public: - explicit PollingThread(const char* name); - virtual ~PollingThread(); - - void StartPolling(DataFetcherSharedMemory* fetcher, - DeviceMotionHardwareBuffer* buffer); - void StopPolling(); - - private: - void DoPoll(); - - scoped_ptr<base::RepeatingTimer<PollingThread> > timer_; - DataFetcherSharedMemory* fetcher_; - - DISALLOW_COPY_AND_ASSIGN(PollingThread); -}; - -// ---- PollingThread methods - -DeviceMotionProvider::PollingThread::PollingThread(const char* name) - : base::Thread(name) { -} - -DeviceMotionProvider::PollingThread::~PollingThread() { -} - -void DeviceMotionProvider::PollingThread::StartPolling( - DataFetcherSharedMemory* fetcher, DeviceMotionHardwareBuffer* buffer) { - DCHECK(base::MessageLoop::current() == message_loop()); - DCHECK(!timer_); - - fetcher_ = fetcher; - fetcher_->StartFetchingDeviceMotionData(buffer); - timer_.reset(new base::RepeatingTimer<PollingThread>()); - timer_->Start(FROM_HERE, - base::TimeDelta::FromMilliseconds(kPeriodInMilliseconds), - this, &PollingThread::DoPoll); -} - -void DeviceMotionProvider::PollingThread::StopPolling() { - DCHECK(base::MessageLoop::current() == message_loop()); - DCHECK(fetcher_); - // this will also stop the timer before killing it. - timer_.reset(); - fetcher_->StopFetchingDeviceMotionData(); -} - -void DeviceMotionProvider::PollingThread::DoPoll() { - DCHECK(base::MessageLoop::current() == message_loop()); - fetcher_->FetchDeviceMotionDataIntoBuffer(); -} - -// ---- end PollingThread methods - -DeviceMotionProvider::DeviceMotionProvider() - : is_started_(false) { - Initialize(); -} - -DeviceMotionProvider::DeviceMotionProvider( - scoped_ptr<DataFetcherSharedMemory> fetcher) - : is_started_(false) { - data_fetcher_ = fetcher.Pass(); - Initialize(); -} - -DeviceMotionProvider::~DeviceMotionProvider() { - StopFetchingDeviceMotionData(); - // make sure polling thread stops before data_fetcher_ gets deleted. - if (polling_thread_) - polling_thread_->Stop(); - data_fetcher_.reset(); -} - -void DeviceMotionProvider::Initialize() { - size_t data_size = sizeof(DeviceMotionHardwareBuffer); - bool res = device_motion_shared_memory_.CreateAndMapAnonymous(data_size); - // TODO(timvolodine): consider not crashing the browser if the check fails. - CHECK(res); - DeviceMotionHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); - memset(hwbuf, 0, sizeof(DeviceMotionHardwareBuffer)); -} - -base::SharedMemoryHandle DeviceMotionProvider::GetSharedMemoryHandleForProcess( - base::ProcessHandle process) { - base::SharedMemoryHandle renderer_handle; - device_motion_shared_memory_.ShareToProcess(process, &renderer_handle); - return renderer_handle; -} - -void DeviceMotionProvider::StartFetchingDeviceMotionData() { - if (is_started_) - return; - - if (!data_fetcher_) - data_fetcher_.reset(new DataFetcherSharedMemory); - - if (data_fetcher_->NeedsPolling()) { - if (!polling_thread_) - CreateAndStartPollingThread(); - - polling_thread_->message_loop()->PostTask( - FROM_HERE, - base::Bind(&PollingThread::StartPolling, - base::Unretained(polling_thread_.get()), - data_fetcher_.get(), - SharedMemoryAsHardwareBuffer())); - } else { - data_fetcher_->StartFetchingDeviceMotionData( - SharedMemoryAsHardwareBuffer()); - } - - is_started_ = true; -} - -void DeviceMotionProvider::CreateAndStartPollingThread() { - polling_thread_.reset( - new PollingThread("Device Motion poller")); - - if (!polling_thread_->Start()) { - LOG(ERROR) << "Failed to start Device Motion data polling thread"; - return; - } -} - -void DeviceMotionProvider::StopFetchingDeviceMotionData() { - if (!is_started_) - return; - - DCHECK(data_fetcher_); - - if (data_fetcher_->NeedsPolling()) { - DCHECK(polling_thread_); - polling_thread_->message_loop()->PostTask( - FROM_HERE, - base::Bind(&PollingThread::StopPolling, - base::Unretained(polling_thread_.get()))); - } else { - data_fetcher_->StopFetchingDeviceMotionData(); - } - - is_started_ = false; -} - -DeviceMotionHardwareBuffer* -DeviceMotionProvider::SharedMemoryAsHardwareBuffer() { - void* mem = device_motion_shared_memory_.memory(); - CHECK(mem); - return static_cast<DeviceMotionHardwareBuffer*>(mem); -} - -} // namespace content diff --git a/chromium/content/browser/device_orientation/device_motion_provider.h b/chromium/content/browser/device_orientation/device_motion_provider.h deleted file mode 100644 index 8fd36de33d6..00000000000 --- a/chromium/content/browser/device_orientation/device_motion_provider.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2013 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_BROWSER_DEVICE_ORIENTATION_DEVICE_MOTION_PROVIDER_H_ -#define CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_MOTION_PROVIDER_H_ - -#include "base/memory/scoped_ptr.h" -#include "base/memory/shared_memory.h" -#include "content/common/content_export.h" -#include "content/common/device_motion_hardware_buffer.h" - -namespace content { -class DataFetcherSharedMemory; - -// This class owns the shared memory buffer for Device Motion and makes -// sure the data is fetched into that buffer. -// When DataFetcherSharedMemory::NeedsPolling() is true, it starts a -// background polling thread to make sure the data is fetched at regular -// intervals. -class CONTENT_EXPORT DeviceMotionProvider { - public: - DeviceMotionProvider(); - - // Creates provider with a custom fetcher. Used for testing. - explicit DeviceMotionProvider(scoped_ptr<DataFetcherSharedMemory> fetcher); - - virtual ~DeviceMotionProvider(); - - // Returns the shared memory handle of the device motion data duplicated - // into the given process. - base::SharedMemoryHandle GetSharedMemoryHandleForProcess( - base::ProcessHandle renderer_process); - - void StartFetchingDeviceMotionData(); - void StopFetchingDeviceMotionData(); - - private: - class PollingThread; - - void Initialize(); - void CreateAndStartPollingThread(); - - DeviceMotionHardwareBuffer* SharedMemoryAsHardwareBuffer(); - - base::SharedMemory device_motion_shared_memory_; - scoped_ptr<DataFetcherSharedMemory> data_fetcher_; - scoped_ptr<PollingThread> polling_thread_; - bool is_started_; - - DISALLOW_COPY_AND_ASSIGN(DeviceMotionProvider); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DEVICE_MOTION_PROVIDER_H_ diff --git a/chromium/content/browser/device_orientation/device_motion_provider_unittest.cc b/chromium/content/browser/device_orientation/device_motion_provider_unittest.cc deleted file mode 100644 index cd0f3d843e3..00000000000 --- a/chromium/content/browser/device_orientation/device_motion_provider_unittest.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2013 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/browser/device_orientation/device_motion_provider.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/waitable_event.h" -#include "content/browser/device_orientation/data_fetcher_shared_memory.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -namespace { - -const int kPeriodInMilliseconds = 100; - -class FakeDataFetcherSharedMemory : public DataFetcherSharedMemory { - public: - FakeDataFetcherSharedMemory() - : start_fetching_data_(false, false), - stop_fetching_data_(false, false), - fetched_data_(false, false) { - } - virtual ~FakeDataFetcherSharedMemory() { } - - virtual bool NeedsPolling() OVERRIDE { - return true; - } - - virtual bool FetchDeviceMotionDataIntoBuffer() OVERRIDE { - buffer_->seqlock.WriteBegin(); - buffer_->data.interval = kPeriodInMilliseconds; - buffer_->seqlock.WriteEnd(); - fetched_data_.Signal(); - return true; - } - - virtual bool StartFetchingDeviceMotionData( - DeviceMotionHardwareBuffer* buffer) OVERRIDE { - buffer_ = buffer; - start_fetching_data_.Signal(); - return true; - } - - virtual void StopFetchingDeviceMotionData() OVERRIDE { - stop_fetching_data_.Signal(); - } - - void WaitForStart() { - start_fetching_data_.Wait(); - } - - void WaitForStop() { - stop_fetching_data_.Wait(); - } - - void WaitForDataFetch() { - fetched_data_.Wait(); - } - - DeviceMotionHardwareBuffer* GetBuffer() { - return buffer_; - } - - private: - base::WaitableEvent start_fetching_data_; - base::WaitableEvent stop_fetching_data_; - base::WaitableEvent fetched_data_; - DeviceMotionHardwareBuffer* buffer_; - - DISALLOW_COPY_AND_ASSIGN(FakeDataFetcherSharedMemory); -}; - - -TEST(DeviceMotionProviderTest, DoesPolling) { - FakeDataFetcherSharedMemory* mock_data_fetcher = - new FakeDataFetcherSharedMemory(); - EXPECT_TRUE(mock_data_fetcher->NeedsPolling()); - - scoped_ptr<DeviceMotionProvider> provider(new DeviceMotionProvider( - scoped_ptr<DataFetcherSharedMemory>(mock_data_fetcher))); - - provider->StartFetchingDeviceMotionData(); - mock_data_fetcher->WaitForStart(); - mock_data_fetcher->WaitForDataFetch(); - - EXPECT_EQ(kPeriodInMilliseconds, - mock_data_fetcher->GetBuffer()->data.interval); - - provider->StopFetchingDeviceMotionData(); - mock_data_fetcher->WaitForStop(); -} - -} // namespace - -} // namespace content diff --git a/chromium/content/browser/device_orientation/device_motion_service.cc b/chromium/content/browser/device_orientation/device_motion_service.cc deleted file mode 100644 index 48b784b925c..00000000000 --- a/chromium/content/browser/device_orientation/device_motion_service.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 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/browser/device_orientation/device_motion_service.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/singleton.h" -#include "content/browser/device_orientation/device_motion_provider.h" -#include "content/public/browser/render_process_host.h" - -namespace content { - -DeviceMotionService::DeviceMotionService() - : num_readers_(0), - is_shutdown_(false) { -} - -DeviceMotionService::~DeviceMotionService() { -} - -DeviceMotionService* DeviceMotionService::GetInstance() { - return Singleton<DeviceMotionService, - LeakySingletonTraits<DeviceMotionService> >::get(); -} - -void DeviceMotionService::AddConsumer() { - DCHECK(thread_checker_.CalledOnValidThread()); - if (is_shutdown_) - return; - - num_readers_++; - DCHECK(num_readers_ > 0); - if (!provider_.get()) - provider_.reset(new DeviceMotionProvider); - provider_->StartFetchingDeviceMotionData(); -} - -void DeviceMotionService::RemoveConsumer() { - DCHECK(thread_checker_.CalledOnValidThread()); - if (is_shutdown_) - return; - - --num_readers_; - DCHECK(num_readers_ >= 0); - - if (num_readers_ == 0) { - LOG(INFO) << "ACTIVE service stop fetching"; - provider_->StopFetchingDeviceMotionData(); - } -} - -void DeviceMotionService::Shutdown() { - provider_.reset(); - is_shutdown_ = true; -} - -base::SharedMemoryHandle DeviceMotionService::GetSharedMemoryHandleForProcess( - base::ProcessHandle handle) { - DCHECK(thread_checker_.CalledOnValidThread()); - return provider_->GetSharedMemoryHandleForProcess(handle); -} - -} // namespace content diff --git a/chromium/content/browser/device_orientation/device_orientation_browsertest.cc b/chromium/content/browser/device_orientation/device_orientation_browsertest.cc index 17d944614d7..92a83c70e0c 100644 --- a/chromium/content/browser/device_orientation/device_orientation_browsertest.cc +++ b/chromium/content/browser/device_orientation/device_orientation_browsertest.cc @@ -11,7 +11,7 @@ #include "content/browser/device_orientation/provider.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" @@ -71,7 +71,7 @@ IN_PROC_BROWSER_TEST_F(DeviceOrientationBrowserTest, BasicTest) { // Check that the page got the event it expected and that the provider // saw requests for adding and removing an observer. - EXPECT_EQ("pass", shell()->web_contents()->GetURL().ref()); + EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); EXPECT_TRUE(provider->added_observer_); EXPECT_TRUE(provider->removed_observer_); } diff --git a/chromium/content/browser/device_orientation/device_orientation_message_filter.cc b/chromium/content/browser/device_orientation/device_orientation_message_filter.cc index cbb1e674c0e..d00f2b8ccfd 100644 --- a/chromium/content/browser/device_orientation/device_orientation_message_filter.cc +++ b/chromium/content/browser/device_orientation/device_orientation_message_filter.cc @@ -4,7 +4,7 @@ #include "content/browser/device_orientation/device_orientation_message_filter.h" -#include "content/browser/device_orientation/device_motion_service.h" +#include "content/browser/device_orientation/device_inertial_sensor_service.h" #include "content/common/device_orientation/device_orientation_messages.h" namespace content { @@ -15,10 +15,9 @@ DeviceOrientationMessageFilter::DeviceOrientationMessageFilter() DeviceOrientationMessageFilter::~DeviceOrientationMessageFilter() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (is_started_) { - // TODO(timvolodine): insert a proper call to DeviceSensorService here, - // similar to DeviceMotionService::GetInstance()->RemoveConsumer(); - } + if (is_started_) + DeviceInertialSensorService::GetInstance()->RemoveConsumer( + CONSUMER_TYPE_ORIENTATION); } bool DeviceOrientationMessageFilter::OnMessageReceived( @@ -38,33 +37,30 @@ bool DeviceOrientationMessageFilter::OnMessageReceived( } void DeviceOrientationMessageFilter::OnDeviceOrientationStartPolling() { - NOTIMPLEMENTED(); DCHECK(!is_started_); if (is_started_) return; is_started_ = true; - // TODO(timvolodine): insert a proper call to DeviceSensorService here, - // similar to DeviceMotionService::GetInstance()->AddConsumer(); + DeviceInertialSensorService::GetInstance()->AddConsumer( + CONSUMER_TYPE_ORIENTATION); DidStartDeviceOrientationPolling(); } void DeviceOrientationMessageFilter::OnDeviceOrientationStopPolling() { - NOTIMPLEMENTED(); DCHECK(is_started_); if (!is_started_) return; is_started_ = false; - // TODO(timvolodine): insert a proper call to DeviceSensorService here, - // similar to DeviceMotionService::GetInstance()->RemoveConsumer(); + DeviceInertialSensorService::GetInstance()->RemoveConsumer( + CONSUMER_TYPE_ORIENTATION); } void DeviceOrientationMessageFilter::DidStartDeviceOrientationPolling() { - NOTIMPLEMENTED(); - // TODO(timvolodine): insert a proper call to the generalized Service here, - // similar to - // Send(new DeviceOrientationMsg_DidStartPolling( - // DeviceMotionService::GetInstance()->GetSharedMemoryHandleForProcess( - // PeerHandle()))); + Send(new DeviceOrientationMsg_DidStartPolling( + DeviceInertialSensorService::GetInstance()-> + GetSharedMemoryHandleForProcess( + CONSUMER_TYPE_ORIENTATION, + PeerHandle()))); } } // namespace content diff --git a/chromium/content/browser/device_orientation/inertial_sensor_consts.h b/chromium/content/browser/device_orientation/inertial_sensor_consts.h new file mode 100644 index 00000000000..312b18b1066 --- /dev/null +++ b/chromium/content/browser/device_orientation/inertial_sensor_consts.h @@ -0,0 +1,19 @@ +// Copyright 2013 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_BROWSER_DEVICE_ORIENTATION_INERTIAL_SENSOR_CONSTS_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_INERTIAL_SENSOR_CONSTS_H_ + +namespace content { + +// Constants related to the Device Motion/Device Orientation APIs. + +enum ConsumerType { + CONSUMER_TYPE_MOTION = 1 << 0, + CONSUMER_TYPE_ORIENTATION = 1 << 1, +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_INERTIAL_SENSOR_CONSTS_H_ diff --git a/chromium/content/browser/devtools/browser_protocol.json b/chromium/content/browser/devtools/browser_protocol.json new file mode 100644 index 00000000000..ecc1bc3624a --- /dev/null +++ b/chromium/content/browser/devtools/browser_protocol.json @@ -0,0 +1,48 @@ +{ + "version": { "major": "1", "minor": "0" }, + "domains": [{ + "domain": "SystemInfo", + "description": "The SystemInfo domain defines methods and events for querying low-level system information.", + "hidden": true, + "types": [ + { + "id": "GPUDevice", + "type": "object", + "properties": [ + { "name": "vendorId", "type": "number", "description": "PCI ID of the GPU vendor, if available; 0 otherwise." }, + { "name": "deviceId", "type": "number", "description": "PCI ID of the GPU device, if available; 0 otherwise." }, + { "name": "vendorString", "type": "string", "description": "String description of the GPU vendor, if the PCI ID is not available." }, + { "name": "deviceString", "type": "string", "description": "String description of the GPU device, if the PCI ID is not available." } + ], + "description": "Describes a single graphics processor (GPU)." + }, + { + "id": "GPUInfo", + "type": "object", + "properties": [ + { "name": "devices", "type": "array", "items": { "$ref": "GPUDevice" }, "description": "The graphics devices on the system. Element 0 is the primary GPU." }, + { "name": "auxAttributes", "type": "object", "optional": "true", "description": "An optional dictionary of additional GPU related attributes." } + ], + "description": "Provides information about the GPU(s) on the system." + }, + { + "id": "SystemInfo", + "type": "object", + "properties": [ + { "name": "gpu", "$ref": "GPUInfo", "description": "Information about the GPUs on the system." }, + { "name": "modelName", "type": "string", "description": "A platform-dependent description of the model of the machine. On Mac OS, this is, for example, 'MacBookPro 10.1'. Will be the empty string if not supported." } + ], + "description": "Provides information about the system." + } + ], + "commands": [ + { + "name": "getInfo", + "description": "Returns information about the system.", + "returns": [ + { "name": "info", "$ref": "SystemInfo", "description": "Information about the system." } + ] + } + ] + }] +} diff --git a/chromium/content/browser/devtools/devtools_frontend_host.cc b/chromium/content/browser/devtools/devtools_frontend_host.cc index 8f177b6941c..bfa623a140e 100644 --- a/chromium/content/browser/devtools/devtools_frontend_host.cc +++ b/chromium/content/browser/devtools/devtools_frontend_host.cc @@ -63,23 +63,8 @@ bool DevToolsFrontendHost::OnMessageReceived( IPC_BEGIN_MESSAGE_MAP(DevToolsFrontendHost, message) IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend, OnDispatchOnInspectorBackend) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_ActivateWindow, OnActivateWindow) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_ChangeAttachedWindowHeight, - OnChangeAttachedWindowHeight) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_CloseWindow, OnCloseWindow) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_MoveWindow, OnMoveWindow) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_RequestSetDockSide, - OnRequestSetDockSide) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_OpenInNewTab, OnOpenInNewTab) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_Save, OnSave) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_Append, OnAppend) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_RequestFileSystems, - OnRequestFileSystems) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_AddFileSystem, OnAddFileSystem) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_RemoveFileSystem, OnRemoveFileSystem) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_IndexPath, OnIndexPath) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_StopIndexing, OnStopIndexing) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_SearchInPath, OnSearchInPath) + IPC_MESSAGE_HANDLER(DevToolsHostMsg_DispatchOnEmbedder, + OnDispatchOnEmbedder) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -101,72 +86,11 @@ void DevToolsFrontendHost::RenderProcessGone( void DevToolsFrontendHost::OnDispatchOnInspectorBackend( const std::string& message) { DevToolsManagerImpl::GetInstance()->DispatchOnInspectorBackend(this, message); -// delegate_->DispatchOnInspectorBackend(message); } -void DevToolsFrontendHost::OnActivateWindow() { - delegate_->ActivateWindow(); -} - -void DevToolsFrontendHost::OnChangeAttachedWindowHeight(unsigned height) { - delegate_->ChangeAttachedWindowHeight(height); -} - -void DevToolsFrontendHost::OnCloseWindow() { - delegate_->CloseWindow(); -} - -void DevToolsFrontendHost::OnMoveWindow(int x, int y) { - delegate_->MoveWindow(x, y); -} - -void DevToolsFrontendHost::OnOpenInNewTab(const std::string& url) { - delegate_->OpenInNewTab(url); -} - -void DevToolsFrontendHost::OnSave( - const std::string& url, - const std::string& content, - bool save_as) { - delegate_->SaveToFile(url, content, save_as); -} - -void DevToolsFrontendHost::OnAppend( - const std::string& url, - const std::string& content) { - delegate_->AppendToFile(url, content); -} - -void DevToolsFrontendHost::OnRequestFileSystems() { - delegate_->RequestFileSystems(); -} - -void DevToolsFrontendHost::OnAddFileSystem() { - delegate_->AddFileSystem(); -} - -void DevToolsFrontendHost::OnRemoveFileSystem( - const std::string& file_system_path) { - delegate_->RemoveFileSystem(file_system_path); -} - -void DevToolsFrontendHost::OnIndexPath(int request_id, - const std::string& file_system_path) { - delegate_->IndexPath(request_id, file_system_path); -} - -void DevToolsFrontendHost::OnStopIndexing(int request_id) { - delegate_->StopIndexing(request_id); -} - -void DevToolsFrontendHost::OnSearchInPath(int request_id, - const std::string& file_system_path, - const std::string& query) { - delegate_->SearchInPath(request_id, file_system_path, query); -} - -void DevToolsFrontendHost::OnRequestSetDockSide(const std::string& side) { - delegate_->SetDockSide(side); +void DevToolsFrontendHost::OnDispatchOnEmbedder( + const std::string& message) { + delegate_->DispatchOnEmbedder(message); } } // namespace content diff --git a/chromium/content/browser/devtools/devtools_frontend_host.h b/chromium/content/browser/devtools/devtools_frontend_host.h index 646ea4bbdce..469d162bf9c 100644 --- a/chromium/content/browser/devtools/devtools_frontend_host.h +++ b/chromium/content/browser/devtools/devtools_frontend_host.h @@ -40,22 +40,7 @@ class DevToolsFrontendHost : public DevToolsClientHost, virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; void OnDispatchOnInspectorBackend(const std::string& message); - void OnActivateWindow(); - void OnChangeAttachedWindowHeight(unsigned height); - void OnCloseWindow(); - void OnMoveWindow(int x, int y); - void OnRequestSetDockSide(const std::string& side); - void OnOpenInNewTab(const std::string& url); - void OnSave(const std::string& url, const std::string& content, bool save_as); - void OnAppend(const std::string& url, const std::string& content); - void OnRequestFileSystems(); - void OnAddFileSystem(); - void OnRemoveFileSystem(const std::string& file_system_path); - void OnIndexPath(int request_id, const std::string& file_system_path); - void OnStopIndexing(int request_id); - void OnSearchInPath(int request_id, - const std::string& file_system_path, - const std::string& query); + void OnDispatchOnEmbedder(const std::string& message); DevToolsFrontendHostDelegate* delegate_; DISALLOW_COPY_AND_ASSIGN(DevToolsFrontendHost); diff --git a/chromium/content/browser/devtools/devtools_http_handler_impl.cc b/chromium/content/browser/devtools/devtools_http_handler_impl.cc index 47bdfd7b9c8..8003b8bd751 100644 --- a/chromium/content/browser/devtools/devtools_http_handler_impl.cc +++ b/chromium/content/browser/devtools/devtools_http_handler_impl.cc @@ -22,6 +22,7 @@ #include "content/browser/devtools/devtools_browser_target.h" #include "content/browser/devtools/devtools_protocol.h" #include "content/browser/devtools/devtools_protocol_constants.h" +#include "content/browser/devtools/devtools_system_info_handler.h" #include "content/browser/devtools/devtools_tracing_handler.h" #include "content/browser/devtools/tethering_handler.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -347,7 +348,7 @@ void DevToolsHttpHandlerImpl::OnHttpRequest( if (!frontend_dir.empty()) { base::FilePath path = frontend_dir.AppendASCII(filename); std::string data; - file_util::ReadFileToString(path, &data); + base::ReadFileToString(path, &data); server_->Send200(connection_id, data, mime_type); return; } @@ -383,6 +384,10 @@ void DevToolsHttpHandlerImpl::OnWebSocketRequest( TetheringHandler::kDomain, new TetheringHandler(delegate_.get()), false /* handle on this thread */); + browser_target_->RegisterDomainHandler( + devtools::SystemInfo::kName, + new DevToolsSystemInfoHandler(), + true /* handle on UI thread */); server_->AcceptWebSocket(connection_id, request); return; diff --git a/chromium/content/browser/devtools/devtools_http_handler_unittest.cc b/chromium/content/browser/devtools/devtools_http_handler_unittest.cc index e770b8fe455..a57c4ccd4cd 100644 --- a/chromium/content/browser/devtools/devtools_http_handler_unittest.cc +++ b/chromium/content/browser/devtools/devtools_http_handler_unittest.cc @@ -23,7 +23,7 @@ class DummyListenSocket : public StreamListenSocket, // StreamListenSocket::Delegate "implementation" virtual void DidAccept(StreamListenSocket* server, - StreamListenSocket* connection) OVERRIDE {} + scoped_ptr<StreamListenSocket> connection) OVERRIDE {} virtual void DidRead(StreamListenSocket* connection, const char* data, int len) OVERRIDE {} @@ -43,11 +43,11 @@ class DummyListenSocketFactory : public net::StreamListenSocketFactory { BrowserThread::UI, FROM_HERE, quit_closure_2_); } - virtual scoped_refptr<StreamListenSocket> CreateAndListen( + virtual scoped_ptr<StreamListenSocket> CreateAndListen( StreamListenSocket::Delegate* delegate) const OVERRIDE { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, quit_closure_1_); - return new DummyListenSocket(); + return scoped_ptr<net::StreamListenSocket>(new DummyListenSocket()); } private: base::Closure quit_closure_1_; @@ -71,10 +71,10 @@ class DummyDelegate : public DevToolsHttpHandlerDelegate { virtual std::string GetViewDescription(content::RenderViewHost*) OVERRIDE { return std::string(); } - virtual scoped_refptr<net::StreamListenSocket> CreateSocketForTethering( + virtual scoped_ptr<net::StreamListenSocket> CreateSocketForTethering( net::StreamListenSocket::Delegate* delegate, std::string* name) OVERRIDE { - return NULL; + return scoped_ptr<net::StreamListenSocket>(); } }; diff --git a/chromium/content/browser/devtools/devtools_protocol.cc b/chromium/content/browser/devtools/devtools_protocol.cc index 83980846556..948ed6be8a4 100644 --- a/chromium/content/browser/devtools/devtools_protocol.cc +++ b/chromium/content/browser/devtools/devtools_protocol.cc @@ -190,6 +190,11 @@ void DevToolsProtocol::Handler::SendNotification( SendRawMessage(notification->Serialize()); } +void DevToolsProtocol::Handler::SendAsyncResponse( + scoped_refptr<DevToolsProtocol::Response> response) { + SendRawMessage(response->Serialize()); +} + void DevToolsProtocol::Handler::SendRawMessage(const std::string& message) { if (!notifier_.is_null()) notifier_.Run(message); diff --git a/chromium/content/browser/devtools/devtools_protocol.h b/chromium/content/browser/devtools/devtools_protocol.h index 0012947382c..2f7ce0bfecb 100644 --- a/chromium/content/browser/devtools/devtools_protocol.h +++ b/chromium/content/browser/devtools/devtools_protocol.h @@ -141,6 +141,8 @@ class DevToolsProtocol { void SendNotification(const std::string& method, base::DictionaryValue* params); + void SendAsyncResponse(scoped_refptr<DevToolsProtocol::Response> response); + // Sends message to client, the caller is presumed to properly // format the message. void SendRawMessage(const std::string& message); diff --git a/chromium/content/browser/devtools/devtools_protocol_constants.cc b/chromium/content/browser/devtools/devtools_protocol_constants.cc index c689ea1b7cb..d9c337fc8aa 100644 --- a/chromium/content/browser/devtools/devtools_protocol_constants.cc +++ b/chromium/content/browser/devtools/devtools_protocol_constants.cc @@ -7,46 +7,132 @@ namespace content { namespace devtools { +const char kParamX[] = "x"; +const char kParamY[] = "y"; +const char kParamWidth[] = "width"; +const char kParamHeight[] = "height"; + +namespace DOM { + +namespace setFileInputFiles { + const char kName[] = "DOM.setFileInputFiles"; + const char kParamFiles[] = "files"; +} // setFileInputFiles + +} // DOM + +namespace Input { + +const char kParamType[] = "type"; +const char kParamModifiers[] = "modifiers"; +const char kParamTimestamp[] = "timestamp"; +const char kParamDeviceSpace[] = "deviceSpace"; + +namespace dispatchMouseEvent { + const char kName[] = "Input.dispatchMouseEvent"; + const char kParamButton[] = "button"; + const char kParamClickCount[] = "clickCount"; +} // dispatchMouseEvent + +namespace dispatchGestureEvent { + const char kName[] = "Input.dispatchGestureEvent"; + const char kParamDeltaX[] = "deltaX"; + const char kParamDeltaY[] = "deltaY"; + const char kParamPinchScale[] = "pinchScale"; +} // dispatchGestureEvent + +} // Input + namespace Inspector { + namespace detached { const char kName[] = "Inspector.detached"; const char kParamReason[] = "reason"; } // detached + namespace targetCrashed { const char kName[] = "Inspector.targetCrashed"; } // targetCrashed -} // Inspector -namespace DOM { -namespace setFileInputFiles { - const char kName[] = "DOM.setFileInputFiles"; - const char kParamFiles[] = "files"; -} // setFileInputFiles -} // DOM +} // Inspector namespace Page { + +const char kData[] = "data"; +const char kParamDeviceScaleFactor[] = "deviceScaleFactor"; +const char kParamPageScaleFactor[] = "pageScaleFactor"; +const char kParamPageScaleFactorMin[] = "pageScaleFactorMin"; +const char kParamPageScaleFactorMax[] = "pageScaleFactorMax"; +const char kParamOffsetBottom[] = "offsetBottom"; +const char kParamOffsetTop[] = "offsetTop"; +const char kParamViewport[] = "viewport"; + +namespace disable { + const char kName[] = "Page.disable"; +} // disable + namespace handleJavaScriptDialog { const char kName[] = "Page.handleJavaScriptDialog"; const char kParamAccept[] = "accept"; const char kParamPromptText[] = "promptText"; } // handleJavaScriptDialog + namespace navigate { const char kName[] = "Page.navigate"; const char kParamUrl[] = "url"; } // navigate + +namespace reload { + const char kName[] = "Page.reload"; +} // reload + +namespace getNavigationHistory { + const char kName[] = "Page.getNavigationHistory"; + const char kResponseCurrentIndex[] = "currentIndex"; + const char kResponseEntries[] = "entries"; + const char kResponseEntryId[] = "id"; + const char kResponseEntryURL[] = "url"; + const char kResponseEntryTitle[] = "title"; +} // getNavigationHistory + +namespace navigateToHistoryEntry { + const char kName[] = "Page.navigateToHistoryEntry"; + const char kParamEntryId[] = "entryId"; +} // navigateToHistoryEntry + namespace captureScreenshot { const char kName[] = "Page.captureScreenshot"; const char kParamFormat[] = "format"; const char kParamQuality[] = "quality"; - const char kParamScale[] = "scale"; - const char kResponseData[] = "data"; + const char kParamMaxWidth[] = "maxWidth"; + const char kParamMaxHeight[] = "maxHeight"; } // captureScreenshot + +namespace startScreencast { + const char kName[] = "Page.startScreencast"; +} // startScreencast + +namespace stopScreencast { + const char kName[] = "Page.stopScreencast"; +} // stopScreencast + +namespace screencastFrame { + const char kName[] = "Page.screencastFrame"; +} // screencastFrame + +namespace screencastVisibilityChanged { + const char kName[] = "Page.screencastVisibilityChanged"; + const char kParamVisible[] = "visible"; +} // screencastVisibilityChanged + } // Page namespace Worker { + namespace disconnectedFromWorker { const char kName[] = "Worker.disconnectedFromWorker"; } // disconnectedFromWorker + } // Worker namespace Tracing { @@ -70,8 +156,17 @@ namespace dataCollected { const char kName[] = "Tracing.dataCollected"; const char kValue[] = "value"; } + } // Tracing +namespace SystemInfo { + const char kName[] = "SystemInfo"; + +namespace getInfo { + const char kName[] = "SystemInfo.getInfo"; +} // getInfo +} // SystemInfo + } // devtools } // content diff --git a/chromium/content/browser/devtools/devtools_protocol_constants.h b/chromium/content/browser/devtools/devtools_protocol_constants.h index a05493318e1..733e697189c 100644 --- a/chromium/content/browser/devtools/devtools_protocol_constants.h +++ b/chromium/content/browser/devtools/devtools_protocol_constants.h @@ -14,72 +14,168 @@ namespace content { namespace devtools { -namespace Inspector { -namespace detached { - extern const char kName[]; - extern const char kParamReason[]; -} // detached -namespace targetCrashed { - extern const char kName[]; -} // targetCrashed -} // Inspector +extern const char kParamX[]; +extern const char kParamY[]; +extern const char kParamWidth[]; +extern const char kParamHeight[]; namespace DOM { -namespace setFileInputFiles { - extern const char kName[]; - extern const char kParamFiles[]; -} // setFileInputFiles + + namespace setFileInputFiles { + extern const char kName[]; + extern const char kParamFiles[]; + } // setFileInputFiles + } // DOM +namespace Input { + + extern const char kParamType[]; + extern const char kParamModifiers[]; + extern const char kParamTimestamp[]; + extern const char kParamDeviceSpace[]; + + namespace dispatchMouseEvent { + extern const char kName[]; + extern const char kParamX[]; + extern const char kParamY[]; + extern const char kParamButton[]; + extern const char kParamClickCount[]; + } // dispatchMouseEvent + + namespace dispatchGestureEvent { + extern const char kName[]; + extern const char kParamDeltaX[]; + extern const char kParamDeltaY[]; + extern const char kParamPinchScale[]; + } // dispatchGestureEvent + +} // Input + +namespace Inspector { + + namespace detached { + extern const char kName[]; + extern const char kParamReason[]; + } // detached + + namespace targetCrashed { + extern const char kName[]; + } // targetCrashed + +} // Inspector + namespace Page { -namespace handleJavaScriptDialog { - extern const char kName[]; - extern const char kParamAccept[]; - extern const char kParamPromptText[]; -} // handleJavaScriptDialog -namespace navigate { - extern const char kName[]; - extern const char kParamUrl[]; -} // navigate -namespace captureScreenshot { - extern const char kName[]; - extern const char kParamFormat[]; - extern const char kParamQuality[]; - extern const char kParamScale[]; - extern const char kResponseData[]; -} // captureScreenshot + + extern const char kData[]; + extern const char kParamDeviceScaleFactor[]; + extern const char kParamPageScaleFactor[]; + extern const char kParamPageScaleFactorMin[]; + extern const char kParamPageScaleFactorMax[]; + extern const char kParamOffsetBottom[]; + extern const char kParamOffsetTop[]; + extern const char kParamViewport[]; + + namespace disable { + extern const char kName[]; + } // disable + + namespace handleJavaScriptDialog { + extern const char kName[]; + extern const char kParamAccept[]; + extern const char kParamPromptText[]; + } // handleJavaScriptDialog + + namespace navigate { + extern const char kName[]; + extern const char kParamUrl[]; + } // navigate + + namespace reload { + extern const char kName[]; + } // reload + + namespace getNavigationHistory { + extern const char kName[]; + extern const char kResponseCurrentIndex[]; + extern const char kResponseEntries[]; + extern const char kResponseEntryId[]; + extern const char kResponseEntryURL[]; + extern const char kResponseEntryTitle[]; + } // getNavigationHistory + + namespace navigateToHistoryEntry { + extern const char kName[]; + extern const char kParamEntryId[]; + } // navigateToHistoryEntry + + namespace captureScreenshot { + extern const char kName[]; + extern const char kParamFormat[]; + extern const char kParamQuality[]; + extern const char kParamMaxWidth[]; + extern const char kParamMaxHeight[]; + } // captureScreenshot + + namespace startScreencast { + extern const char kName[]; + } // startScreencast + + namespace stopScreencast { + extern const char kName[]; + } // stopScreencast + + namespace screencastFrame { + extern const char kName[]; + } // screencastFrame + + namespace screencastVisibilityChanged { + extern const char kName[]; + extern const char kParamVisible[]; + } // screencastVisibilityChanged } // Page namespace Tracing { extern const char kName[]; -namespace start { - extern const char kName[]; - extern const char kCategories[]; - extern const char kTraceOptions[]; -} // start + namespace start { + extern const char kName[]; + extern const char kCategories[]; + extern const char kTraceOptions[]; + } // start -namespace end { - extern const char kName[]; -} + namespace end { + extern const char kName[]; + } -namespace tracingComplete { - extern const char kName[]; -} + namespace tracingComplete { + extern const char kName[]; + } -namespace dataCollected { - extern const char kName[]; - extern const char kValue[]; -} -} // Tracing + namespace dataCollected { + extern const char kName[]; + extern const char kValue[]; + } +} // Tracing namespace Worker { -namespace disconnectedFromWorker { - extern const char kName[]; -} // disconnectedFromWorker + + namespace disconnectedFromWorker { + extern const char kName[]; + } // disconnectedFromWorker + } // Worker + +namespace SystemInfo { + extern const char kName[]; + +namespace getInfo { + extern const char kName[]; +} // getInfo +} // SystemInfo + } // devtools } // content diff --git a/chromium/content/browser/devtools/devtools_system_info_handler.cc b/chromium/content/browser/devtools/devtools_system_info_handler.cc new file mode 100644 index 00000000000..1fee6424676 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_system_info_handler.cc @@ -0,0 +1,123 @@ +// Copyright 2013 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/browser/devtools/devtools_system_info_handler.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/values.h" +#include "content/browser/devtools/devtools_protocol_constants.h" +#include "content/browser/gpu/gpu_data_manager_impl.h" +#include "gpu/config/gpu_info.h" + +namespace content { + +namespace { + +const char kAuxAttributes[] = "auxAttributes"; +const char kDeviceId[] = "deviceId"; +const char kDeviceString[] = "deviceString"; +const char kDevices[] = "devices"; +const char kGPU[] = "gpu"; +const char kModelName[] = "modelName"; +const char kVendorId[] = "vendorId"; +const char kVendorString[] = "vendorString"; + +class AuxGPUInfoEnumerator : public gpu::GPUInfo::Enumerator { + public: + AuxGPUInfoEnumerator(base::DictionaryValue* dictionary) + : dictionary_(dictionary), + in_aux_attributes_(false) { } + + virtual void AddInt64(const char* name, int64 value) OVERRIDE { + if (in_aux_attributes_) + dictionary_->SetDouble(name, value); + } + + virtual void AddInt(const char* name, int value) OVERRIDE { + if (in_aux_attributes_) + dictionary_->SetInteger(name, value); + } + + virtual void AddString(const char* name, const std::string& value) OVERRIDE { + if (in_aux_attributes_) + dictionary_->SetString(name, value); + } + + virtual void AddBool(const char* name, bool value) OVERRIDE { + if (in_aux_attributes_) + dictionary_->SetBoolean(name, value); + } + + virtual void AddTimeDeltaInSecondsF(const char* name, + const base::TimeDelta& value) OVERRIDE { + if (in_aux_attributes_) + dictionary_->SetDouble(name, value.InSecondsF()); + } + + virtual void BeginGPUDevice() OVERRIDE { + } + + virtual void EndGPUDevice() OVERRIDE { + } + + virtual void BeginAuxAttributes() OVERRIDE { + in_aux_attributes_ = true; + } + + virtual void EndAuxAttributes() OVERRIDE { + in_aux_attributes_ = false; + } + + private: + base::DictionaryValue* dictionary_; + bool in_aux_attributes_; +}; + +base::DictionaryValue* GPUDeviceToDictionary( + const gpu::GPUInfo::GPUDevice& device) { + base::DictionaryValue* result = new base::DictionaryValue; + result->SetInteger(kVendorId, device.vendor_id); + result->SetInteger(kDeviceId, device.device_id); + result->SetString(kVendorString, device.vendor_string); + result->SetString(kDeviceString, device.device_string); + return result; +} + +} // namespace + +DevToolsSystemInfoHandler::DevToolsSystemInfoHandler() { + RegisterCommandHandler(devtools::SystemInfo::getInfo::kName, + base::Bind(&DevToolsSystemInfoHandler::OnGetInfo, + base::Unretained(this))); +} + +DevToolsSystemInfoHandler::~DevToolsSystemInfoHandler() { +} + +scoped_refptr<DevToolsProtocol::Response> +DevToolsSystemInfoHandler::OnGetInfo( + scoped_refptr<DevToolsProtocol::Command> command) { + gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo(); + base::DictionaryValue* gpu_dict = new base::DictionaryValue; + + base::ListValue* devices = new base::ListValue; + devices->Append(GPUDeviceToDictionary(gpu_info.gpu)); + for (size_t ii = 0; ii < gpu_info.secondary_gpus.size(); ++ii) { + devices->Append(GPUDeviceToDictionary(gpu_info.secondary_gpus[ii])); + } + gpu_dict->Set(kDevices, devices); + + base::DictionaryValue* aux_attributes = new base::DictionaryValue; + AuxGPUInfoEnumerator enumerator(aux_attributes); + gpu_info.EnumerateFields(&enumerator); + gpu_dict->Set(kAuxAttributes, aux_attributes); + + base::DictionaryValue* system_dict = new base::DictionaryValue; + system_dict->SetString(kModelName, gpu_info.machine_model); + system_dict->Set(kGPU, gpu_dict); + return command->SuccessResponse(system_dict); +} + +} // namespace content diff --git a/chromium/content/browser/devtools/devtools_system_info_handler.h b/chromium/content/browser/devtools/devtools_system_info_handler.h new file mode 100644 index 00000000000..afaf042b5b6 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_system_info_handler.h @@ -0,0 +1,28 @@ +// Copyright 2013 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_BROWSER_DEVTOOLS_DEVTOOLS_SYSTEM_INFO_HANDLER_H_ +#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_SYSTEM_INFO_HANDLER_H_ + +#include "content/browser/devtools/devtools_protocol.h" + +namespace content { + +// This class provides information to DevTools about the system it's running on. +class DevToolsSystemInfoHandler + : public DevToolsProtocol::Handler { + public: + DevToolsSystemInfoHandler(); + virtual ~DevToolsSystemInfoHandler(); + + private: + scoped_refptr<DevToolsProtocol::Response> OnGetInfo( + scoped_refptr<DevToolsProtocol::Command> command); + + DISALLOW_COPY_AND_ASSIGN(DevToolsSystemInfoHandler); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_SYSTEM_INFO_HANDLER_H_ diff --git a/chromium/content/browser/devtools/render_view_devtools_agent_host.cc b/chromium/content/browser/devtools/render_view_devtools_agent_host.cc index 7455c160d8e..5570ee839ed 100644 --- a/chromium/content/browser/devtools/render_view_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/render_view_devtools_agent_host.cc @@ -17,9 +17,11 @@ #include "content/browser/site_instance_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/devtools_messages.h" +#include "content/common/view_messages.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/render_widget_host_iterator.h" namespace content { @@ -100,15 +102,16 @@ bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) { //static std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() { std::vector<RenderViewHost*> result; - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { // Ignore processes that don't have a connection, such as crashed contents. - if (!widgets[i]->GetProcess()->HasConnection()) + if (!widget->GetProcess()->HasConnection()) continue; - if (!widgets[i]->IsRenderView()) + if (!widget->IsRenderView()) continue; - RenderViewHost* rvh = RenderViewHost::From(widgets[i]); + RenderViewHost* rvh = RenderViewHost::From(widget); WebContents* web_contents = WebContents::FromRenderViewHost(rvh); // Don't report a RenderViewHost if it is not the current RenderViewHost // for some WebContents. @@ -133,7 +136,8 @@ void RenderViewDevToolsAgentHost::OnCancelPendingNavigation( RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost( RenderViewHost* rvh) - : overrides_handler_(new RendererOverridesHandler(this)), + : render_view_host_(NULL), + overrides_handler_(new RendererOverridesHandler(this)), tracing_handler_(new DevToolsTracingHandler()) { SetRenderViewHost(rvh); @@ -145,7 +149,7 @@ RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost( g_instances.Get().push_back(this); RenderViewHostDelegate* delegate = render_view_host_->GetDelegate(); if (delegate && delegate->GetAsWebContents()) - Observe(delegate->GetAsWebContents()); + WebContentsObserver::Observe(delegate->GetAsWebContents()); AddRef(); // Balanced in RenderViewHostDestroyed. } @@ -194,6 +198,11 @@ void RenderViewDevToolsAgentHost::OnClientAttached() { } void RenderViewDevToolsAgentHost::OnClientDetached() { + overrides_handler_->OnClientDetached(); + ClientDetachedFromRenderer(); +} + +void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() { if (!render_view_host_) return; @@ -266,9 +275,33 @@ void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() { ConnectRenderViewHost(web_contents->GetRenderViewHost()); } +void RenderViewDevToolsAgentHost::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) { + bool visible = *Details<bool>(details).ptr(); + overrides_handler_->OnVisibilityChanged(visible); + } +} + void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) { + DCHECK(!render_view_host_); render_view_host_ = rvh; rvh_observer_.reset(new DevToolsAgentHostRvhObserver(rvh, this)); + registrar_.Add( + this, + content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, + content::Source<RenderWidgetHost>(render_view_host_)); +} + +void RenderViewDevToolsAgentHost::ClearRenderViewHost() { + DCHECK(render_view_host_); + registrar_.Remove( + this, + content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, + content::Source<RenderWidgetHost>(render_view_host_)); + rvh_observer_.reset(NULL); + render_view_host_ = NULL; } void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) { @@ -278,9 +311,8 @@ void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) { } void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() { - OnClientDetached(); - rvh_observer_.reset(); - render_view_host_ = NULL; + ClientDetachedFromRenderer(); + ClearRenderViewHost(); } void RenderViewDevToolsAgentHost::RenderViewHostDestroyed( @@ -288,7 +320,7 @@ void RenderViewDevToolsAgentHost::RenderViewHostDestroyed( DCHECK(render_view_host_); scoped_refptr<RenderViewDevToolsAgentHost> protect(this); NotifyCloseListener(); - render_view_host_ = NULL; + ClearRenderViewHost(); Release(); } @@ -301,9 +333,9 @@ void RenderViewDevToolsAgentHost::RenderViewCrashed() { } bool RenderViewDevToolsAgentHost::OnRvhMessageReceived( - const IPC::Message& message) { + const IPC::Message& msg) { bool handled = true; - IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, message) + IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, msg) IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, OnDispatchOnInspectorFrontend) IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState, @@ -311,11 +343,18 @@ bool RenderViewDevToolsAgentHost::OnRvhMessageReceived( IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCache, OnClearBrowserCache) IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCookies, OnClearBrowserCookies) + IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame, + handled = false; OnSwapCompositorFrame(msg)) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } +void RenderViewDevToolsAgentHost::OnSwapCompositorFrame( + const IPC::Message& message) { + overrides_handler_->OnSwapCompositorFrame(message); +} + void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState( const std::string& state) { if (!render_view_host_) diff --git a/chromium/content/browser/devtools/render_view_devtools_agent_host.h b/chromium/content/browser/devtools/render_view_devtools_agent_host.h index 00f73a6b2a0..3f763cabb36 100644 --- a/chromium/content/browser/devtools/render_view_devtools_agent_host.h +++ b/chromium/content/browser/devtools/render_view_devtools_agent_host.h @@ -12,6 +12,8 @@ #include "base/memory/scoped_ptr.h" #include "content/browser/devtools/ipc_devtools_agent_host.h" #include "content/common/content_export.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" #include "content/public/browser/render_view_host_observer.h" #include "content/public/browser/web_contents_observer.h" @@ -23,7 +25,8 @@ class RenderViewHost; class CONTENT_EXPORT RenderViewDevToolsAgentHost : public IPCDevToolsAgentHost, - private WebContentsObserver { + private WebContentsObserver, + public NotificationObserver { public: static void OnCancelPendingNavigation(RenderViewHost* pending, RenderViewHost* current); @@ -54,22 +57,32 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; virtual void DidAttachInterstitialPage() OVERRIDE; + // NotificationObserver overrides: + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + void SetRenderViewHost(RenderViewHost* rvh); + void ClearRenderViewHost(); void RenderViewHostDestroyed(RenderViewHost* rvh); void RenderViewCrashed(); bool OnRvhMessageReceived(const IPC::Message& message); + void OnSwapCompositorFrame(const IPC::Message& message); void OnDispatchOnInspectorFrontend(const std::string& message); void OnSaveAgentRuntimeState(const std::string& state); void OnClearBrowserCache(); void OnClearBrowserCookies(); + void ClientDetachedFromRenderer(); + RenderViewHost* render_view_host_; scoped_ptr<DevToolsAgentHostRvhObserver> rvh_observer_; scoped_ptr<RendererOverridesHandler> overrides_handler_; scoped_ptr<DevToolsTracingHandler> tracing_handler_; std::string state_; + NotificationRegistrar registrar_; DISALLOW_COPY_AND_ASSIGN(RenderViewDevToolsAgentHost); }; diff --git a/chromium/content/browser/devtools/renderer_overrides_handler.cc b/chromium/content/browser/devtools/renderer_overrides_handler.cc index 57266676e87..c25adde7619 100644 --- a/chromium/content/browser/devtools/renderer_overrides_handler.cc +++ b/chromium/content/browser/devtools/renderer_overrides_handler.cc @@ -15,12 +15,16 @@ #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/devtools/devtools_protocol_constants.h" #include "content/browser/devtools/devtools_tracing_handler.h" +#include "content/browser/renderer_host/dip_util.h" #include "content/browser/renderer_host/render_view_host_delegate.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/common/view_messages.h" #include "content/port/browser/render_widget_host_view_port.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/javascript_dialog_manager.h" #include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_view.h" @@ -28,23 +32,47 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/page_transition_types.h" #include "content/public/common/referrer.h" +#include "ipc/ipc_sender.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/size_conversions.h" #include "ui/snapshot/snapshot.h" #include "url/gurl.h" -using base::TimeTicks; +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; + +namespace content { namespace { static const char kPng[] = "png"; static const char kJpeg[] = "jpeg"; static int kDefaultScreenshotQuality = 80; +static int kFrameRateThresholdMs = 100; -} // namespace +void ParseGenericInputParams(base::DictionaryValue* params, + WebInputEvent* event) { + int modifiers = 0; + if (params->GetInteger(devtools::Input::kParamModifiers, + &modifiers)) { + if (modifiers & 1) + event->modifiers |= WebInputEvent::AltKey; + if (modifiers & 2) + event->modifiers |= WebInputEvent::ControlKey; + if (modifiers & 4) + event->modifiers |= WebInputEvent::MetaKey; + if (modifiers & 8) + event->modifiers |= WebInputEvent::ShiftKey; + } -namespace content { + params->GetDouble(devtools::Input::kParamTimestamp, + &event->timeStampSeconds); +} + +} // namespace RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent) : agent_(agent), @@ -55,6 +83,10 @@ RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent) &RendererOverridesHandler::GrantPermissionsForSetFileInputFiles, base::Unretained(this))); RegisterCommandHandler( + devtools::Page::disable::kName, + base::Bind( + &RendererOverridesHandler::PageDisable, base::Unretained(this))); + RegisterCommandHandler( devtools::Page::handleJavaScriptDialog::kName, base::Bind( &RendererOverridesHandler::PageHandleJavaScriptDialog, @@ -65,14 +97,141 @@ RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent) &RendererOverridesHandler::PageNavigate, base::Unretained(this))); RegisterCommandHandler( + devtools::Page::reload::kName, + base::Bind( + &RendererOverridesHandler::PageReload, + base::Unretained(this))); + RegisterCommandHandler( + devtools::Page::getNavigationHistory::kName, + base::Bind( + &RendererOverridesHandler::PageGetNavigationHistory, + base::Unretained(this))); + RegisterCommandHandler( + devtools::Page::navigateToHistoryEntry::kName, + base::Bind( + &RendererOverridesHandler::PageNavigateToHistoryEntry, + base::Unretained(this))); + RegisterCommandHandler( devtools::Page::captureScreenshot::kName, base::Bind( &RendererOverridesHandler::PageCaptureScreenshot, base::Unretained(this))); + RegisterCommandHandler( + devtools::Page::startScreencast::kName, + base::Bind( + &RendererOverridesHandler::PageStartScreencast, + base::Unretained(this))); + RegisterCommandHandler( + devtools::Page::stopScreencast::kName, + base::Bind( + &RendererOverridesHandler::PageStopScreencast, + base::Unretained(this))); + RegisterCommandHandler( + devtools::Input::dispatchMouseEvent::kName, + base::Bind( + &RendererOverridesHandler::InputDispatchMouseEvent, + base::Unretained(this))); + RegisterCommandHandler( + devtools::Input::dispatchGestureEvent::kName, + base::Bind( + &RendererOverridesHandler::InputDispatchGestureEvent, + base::Unretained(this))); } RendererOverridesHandler::~RendererOverridesHandler() {} +void RendererOverridesHandler::OnClientDetached() { + screencast_command_ = NULL; +} + +void RendererOverridesHandler::OnSwapCompositorFrame( + const IPC::Message& message) { + ViewHostMsg_SwapCompositorFrame::Param param; + if (!ViewHostMsg_SwapCompositorFrame::Read(&message, ¶m)) + return; + last_compositor_frame_metadata_ = param.b.metadata; + + if (screencast_command_) + InnerSwapCompositorFrame(); +} + +void RendererOverridesHandler::OnVisibilityChanged(bool visible) { + if (!screencast_command_) + return; + NotifyScreencastVisibility(visible); +} + +void RendererOverridesHandler::InnerSwapCompositorFrame() { + if ((base::TimeTicks::Now() - last_frame_time_).InMilliseconds() < + kFrameRateThresholdMs) { + return; + } + + last_frame_time_ = base::TimeTicks::Now(); + std::string format; + int quality = kDefaultScreenshotQuality; + double scale = 1; + ParseCaptureParameters(screencast_command_.get(), &format, &quality, &scale); + + RenderViewHost* host = agent_->GetRenderViewHost(); + RenderWidgetHostViewPort* view_port = + RenderWidgetHostViewPort::FromRWHV(host->GetView()); + + gfx::Rect view_bounds = host->GetView()->GetViewBounds(); + gfx::Size snapshot_size = gfx::ToFlooredSize( + gfx::ScaleSize(view_bounds.size(), scale)); + + view_port->CopyFromCompositingSurface( + view_bounds, snapshot_size, + base::Bind(&RendererOverridesHandler::ScreenshotCaptured, + weak_factory_.GetWeakPtr(), + scoped_refptr<DevToolsProtocol::Command>(), format, quality, + last_compositor_frame_metadata_)); +} + +void RendererOverridesHandler::ParseCaptureParameters( + DevToolsProtocol::Command* command, + std::string* format, + int* quality, + double* scale) { + RenderViewHost* host = agent_->GetRenderViewHost(); + gfx::Rect view_bounds = host->GetView()->GetViewBounds(); + + *quality = kDefaultScreenshotQuality; + *scale = 1; + double max_width = -1; + double max_height = -1; + base::DictionaryValue* params = command->params(); + if (params) { + params->GetString(devtools::Page::captureScreenshot::kParamFormat, + format); + params->GetInteger(devtools::Page::captureScreenshot::kParamQuality, + quality); + params->GetDouble(devtools::Page::captureScreenshot::kParamMaxWidth, + &max_width); + params->GetDouble(devtools::Page::captureScreenshot::kParamMaxHeight, + &max_height); + } + + float device_sf = last_compositor_frame_metadata_.device_scale_factor; + + if (max_width > 0) + *scale = std::min(*scale, max_width / view_bounds.width() / device_sf); + if (max_height > 0) + *scale = std::min(*scale, max_height / view_bounds.height() / device_sf); + + if (format->empty()) + *format = kPng; + if (*quality < 0 || *quality > 100) + *quality = kDefaultScreenshotQuality; + if (*scale <= 0) + *scale = 0.1; + if (*scale > 5) + *scale = 5; +} + +// DOM agent handlers -------------------------------------------------------- + scoped_refptr<DevToolsProtocol::Response> RendererOverridesHandler::GrantPermissionsForSetFileInputFiles( scoped_refptr<DevToolsProtocol::Command> command) { @@ -96,6 +255,16 @@ RendererOverridesHandler::GrantPermissionsForSetFileInputFiles( return NULL; } + +// Page agent handlers ------------------------------------------------------- + +scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::PageDisable( + scoped_refptr<DevToolsProtocol::Command> command) { + screencast_command_ = NULL; + return NULL; +} + scoped_refptr<DevToolsProtocol::Response> RendererOverridesHandler::PageHandleJavaScriptDialog( scoped_refptr<DevToolsProtocol::Command> command) { @@ -153,27 +322,94 @@ RendererOverridesHandler::PageNavigate( } scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::PageReload( + scoped_refptr<DevToolsProtocol::Command> command) { + RenderViewHost* host = agent_->GetRenderViewHost(); + if (host) { + WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); + if (web_contents) { + // Override only if it is crashed. + if (!web_contents->IsCrashed()) + return NULL; + + web_contents->GetController().Reload(false); + return command->SuccessResponse(NULL); + } + } + return command->InternalErrorResponse("No WebContents to reload"); +} + +scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::PageGetNavigationHistory( + scoped_refptr<DevToolsProtocol::Command> command) { + RenderViewHost* host = agent_->GetRenderViewHost(); + if (host) { + WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); + if (web_contents) { + base::DictionaryValue* result = new base::DictionaryValue(); + NavigationController& controller = web_contents->GetController(); + result->SetInteger( + devtools::Page::getNavigationHistory::kResponseCurrentIndex, + controller.GetCurrentEntryIndex()); + ListValue* entries = new ListValue(); + for (int i = 0; i != controller.GetEntryCount(); ++i) { + const NavigationEntry* entry = controller.GetEntryAtIndex(i); + base::DictionaryValue* entry_value = new base::DictionaryValue(); + entry_value->SetInteger( + devtools::Page::getNavigationHistory::kResponseEntryId, + entry->GetUniqueID()); + entry_value->SetString( + devtools::Page::getNavigationHistory::kResponseEntryURL, + entry->GetURL().spec()); + entry_value->SetString( + devtools::Page::getNavigationHistory::kResponseEntryTitle, + entry->GetTitle()); + entries->Append(entry_value); + } + result->Set( + devtools::Page::getNavigationHistory::kResponseEntries, + entries); + return command->SuccessResponse(result); + } + } + return command->InternalErrorResponse("No WebContents to navigate"); +} + +scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::PageNavigateToHistoryEntry( + scoped_refptr<DevToolsProtocol::Command> command) { + int entry_id; + + base::DictionaryValue* params = command->params(); + const char* param = devtools::Page::navigateToHistoryEntry::kParamEntryId; + if (!params || !params->GetInteger(param, &entry_id)) { + return command->InvalidParamResponse(param); + } + + RenderViewHost* host = agent_->GetRenderViewHost(); + if (host) { + WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); + if (web_contents) { + NavigationController& controller = web_contents->GetController(); + for (int i = 0; i != controller.GetEntryCount(); ++i) { + if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) { + controller.GoToIndex(i); + return command->SuccessResponse(new base::DictionaryValue()); + } + } + return command->InvalidParamResponse(param); + } + } + return command->InternalErrorResponse("No WebContents to navigate"); +} + +scoped_refptr<DevToolsProtocol::Response> RendererOverridesHandler::PageCaptureScreenshot( scoped_refptr<DevToolsProtocol::Command> command) { - // Parse input parameters. std::string format; int quality = kDefaultScreenshotQuality; double scale = 1; - base::DictionaryValue* params = command->params(); - if (params) { - params->GetString(devtools::Page::captureScreenshot::kParamFormat, - &format); - params->GetInteger(devtools::Page::captureScreenshot::kParamQuality, - &quality); - params->GetDouble(devtools::Page::captureScreenshot::kParamScale, - &scale); - } - if (format.empty()) - format = kPng; - if (quality < 0 || quality > 100) - quality = kDefaultScreenshotQuality; - if (scale <= 0 || scale > 1) - scale = 1; + ParseCaptureParameters(command.get(), &format, &quality, &scale); RenderViewHost* host = agent_->GetRenderViewHost(); gfx::Rect view_bounds = host->GetView()->GetViewBounds(); @@ -191,7 +427,7 @@ RendererOverridesHandler::PageCaptureScreenshot( if (success) { base::DictionaryValue* result = new base::DictionaryValue(); result->SetString( - devtools::Page::captureScreenshot::kResponseData, base64_data); + devtools::Page::kData, base64_data); return command->SuccessResponse(result); } return command->InternalErrorResponse("Unable to base64encode screenshot"); @@ -206,21 +442,44 @@ RendererOverridesHandler::PageCaptureScreenshot( view_port->CopyFromCompositingSurface( view_bounds, snapshot_size, base::Bind(&RendererOverridesHandler::ScreenshotCaptured, - weak_factory_.GetWeakPtr(), command, format, quality, scale)); + weak_factory_.GetWeakPtr(), command, format, quality, + last_compositor_frame_metadata_)); return command->AsyncResponsePromise(); } +scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::PageStartScreencast( + scoped_refptr<DevToolsProtocol::Command> command) { + screencast_command_ = command; + RenderViewHostImpl* host = static_cast<RenderViewHostImpl*>( + agent_->GetRenderViewHost()); + bool visible = !host->is_hidden(); + NotifyScreencastVisibility(visible); + if (visible) + InnerSwapCompositorFrame(); + return command->SuccessResponse(NULL); +} + +scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::PageStopScreencast( + scoped_refptr<DevToolsProtocol::Command> command) { + last_frame_time_ = base::TimeTicks(); + screencast_command_ = NULL; + return command->SuccessResponse(NULL); +} + void RendererOverridesHandler::ScreenshotCaptured( scoped_refptr<DevToolsProtocol::Command> command, const std::string& format, int quality, - double scale, + const cc::CompositorFrameMetadata& metadata, bool success, const SkBitmap& bitmap) { if (!success) { - SendRawMessage( - command->InternalErrorResponse("Unable to capture screenshot")-> - Serialize()); + if (command) { + SendAsyncResponse( + command->InternalErrorResponse("Unable to capture screenshot")); + } return; } @@ -247,27 +506,209 @@ void RendererOverridesHandler::ScreenshotCaptured( } if (!encoded) { - SendRawMessage( - command->InternalErrorResponse("Unable to encode screenshot")-> - Serialize()); + if (command) { + SendAsyncResponse( + command->InternalErrorResponse("Unable to encode screenshot")); + } return; } std::string base_64_data; if (!base::Base64Encode(base::StringPiece( - reinterpret_cast<char*>(&data[0]), - data.size()), + reinterpret_cast<char*>(&data[0]), + data.size()), &base_64_data)) { - SendRawMessage( - command->InternalErrorResponse("Unable to base64 encode screenshot")-> - Serialize()); + if (command) { + SendAsyncResponse( + command->InternalErrorResponse("Unable to base64 encode")); + } return; } base::DictionaryValue* response = new base::DictionaryValue(); - response->SetString( - devtools::Page::captureScreenshot::kResponseData, base_64_data); - SendRawMessage(command->SuccessResponse(response)->Serialize()); + response->SetString(devtools::Page::kData, base_64_data); + + // Consider metadata empty in case it has no device scale factor. + if (metadata.device_scale_factor != 0) { + response->SetDouble(devtools::Page::kParamDeviceScaleFactor, + metadata.device_scale_factor); + response->SetDouble(devtools::Page::kParamPageScaleFactor, + metadata.page_scale_factor); + response->SetDouble(devtools::Page::kParamPageScaleFactorMin, + metadata.min_page_scale_factor); + response->SetDouble(devtools::Page::kParamPageScaleFactorMax, + metadata.max_page_scale_factor); + response->SetDouble(devtools::Page::kParamOffsetTop, + metadata.location_bar_content_translation.y()); + response->SetDouble(devtools::Page::kParamOffsetBottom, + metadata.overdraw_bottom_height); + + base::DictionaryValue* viewport = new base::DictionaryValue(); + viewport->SetDouble(devtools::kParamX, metadata.root_scroll_offset.x()); + viewport->SetDouble(devtools::kParamY, metadata.root_scroll_offset.y()); + viewport->SetDouble(devtools::kParamWidth, metadata.viewport_size.width()); + viewport->SetDouble(devtools::kParamHeight, + metadata.viewport_size.height()); + response->Set(devtools::Page::kParamViewport, viewport); + } + + if (command) { + SendAsyncResponse(command->SuccessResponse(response)); + } else { + SendNotification(devtools::Page::screencastFrame::kName, response); + } +} + +void RendererOverridesHandler::NotifyScreencastVisibility(bool visible) { + base::DictionaryValue* params = new base::DictionaryValue(); + params->SetBoolean( + devtools::Page::screencastVisibilityChanged::kParamVisible, visible); + SendNotification( + devtools::Page::screencastVisibilityChanged::kName, params); +} + +// Input agent handlers ------------------------------------------------------ + +scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::InputDispatchMouseEvent( + scoped_refptr<DevToolsProtocol::Command> command) { + base::DictionaryValue* params = command->params(); + if (!params) + return NULL; + + bool device_space = false; + if (!params->GetBoolean(devtools::Input::kParamDeviceSpace, + &device_space) || + !device_space) { + return NULL; + } + + RenderViewHost* host = agent_->GetRenderViewHost(); + WebKit::WebMouseEvent mouse_event; + ParseGenericInputParams(params, &mouse_event); + + std::string type; + if (params->GetString(devtools::Input::kParamType, + &type)) { + if (type == "mousePressed") + mouse_event.type = WebInputEvent::MouseDown; + else if (type == "mouseReleased") + mouse_event.type = WebInputEvent::MouseUp; + else if (type == "mouseMoved") + mouse_event.type = WebInputEvent::MouseMove; + else + return NULL; + } else { + return NULL; + } + + if (!params->GetInteger(devtools::kParamX, &mouse_event.x) || + !params->GetInteger(devtools::kParamY, &mouse_event.y)) { + return NULL; + } + + mouse_event.windowX = mouse_event.x; + mouse_event.windowY = mouse_event.y; + mouse_event.globalX = mouse_event.x; + mouse_event.globalY = mouse_event.y; + + params->GetInteger(devtools::Input::dispatchMouseEvent::kParamClickCount, + &mouse_event.clickCount); + + std::string button; + if (!params->GetString(devtools::Input::dispatchMouseEvent::kParamButton, + &button)) { + return NULL; + } + + if (button == "none") { + mouse_event.button = WebMouseEvent::ButtonNone; + } else if (button == "left") { + mouse_event.button = WebMouseEvent::ButtonLeft; + mouse_event.modifiers |= WebInputEvent::LeftButtonDown; + } else if (button == "middle") { + mouse_event.button = WebMouseEvent::ButtonMiddle; + mouse_event.modifiers |= WebInputEvent::MiddleButtonDown; + } else if (button == "right") { + mouse_event.button = WebMouseEvent::ButtonRight; + mouse_event.modifiers |= WebInputEvent::RightButtonDown; + } else { + return NULL; + } + + host->ForwardMouseEvent(mouse_event); + return command->SuccessResponse(NULL); +} + +scoped_refptr<DevToolsProtocol::Response> +RendererOverridesHandler::InputDispatchGestureEvent( + scoped_refptr<DevToolsProtocol::Command> command) { + base::DictionaryValue* params = command->params(); + if (!params) + return NULL; + + RenderViewHostImpl* host = static_cast<RenderViewHostImpl*>( + agent_->GetRenderViewHost()); + WebKit::WebGestureEvent event; + ParseGenericInputParams(params, &event); + + std::string type; + if (params->GetString(devtools::Input::kParamType, + &type)) { + if (type == "scrollBegin") + event.type = WebInputEvent::GestureScrollBegin; + else if (type == "scrollUpdate") + event.type = WebInputEvent::GestureScrollUpdate; + else if (type == "scrollEnd") + event.type = WebInputEvent::GestureScrollEnd; + else if (type == "tapDown") + event.type = WebInputEvent::GestureTapDown; + else if (type == "tap") + event.type = WebInputEvent::GestureTap; + else if (type == "pinchBegin") + event.type = WebInputEvent::GesturePinchBegin; + else if (type == "pinchUpdate") + event.type = WebInputEvent::GesturePinchUpdate; + else if (type == "pinchEnd") + event.type = WebInputEvent::GesturePinchEnd; + else + return NULL; + } else { + return NULL; + } + + if (!params->GetInteger(devtools::kParamX, &event.x) || + !params->GetInteger(devtools::kParamY, &event.y)) { + return NULL; + } + event.globalX = event.x; + event.globalY = event.y; + + if (type == "scrollUpdate") { + int dx; + int dy; + if (!params->GetInteger( + devtools::Input::dispatchGestureEvent::kParamDeltaX, &dx) || + !params->GetInteger( + devtools::Input::dispatchGestureEvent::kParamDeltaY, &dy)) { + return NULL; + } + event.data.scrollUpdate.deltaX = dx; + event.data.scrollUpdate.deltaY = dy; + } + + if (type == "pinchUpdate") { + double scale; + if (!params->GetDouble( + devtools::Input::dispatchGestureEvent::kParamPinchScale, + &scale)) { + return NULL; + } + event.data.pinchUpdate.scale = static_cast<float>(scale); + } + + host->ForwardGestureEvent(event); + return command->SuccessResponse(NULL); } } // namespace content diff --git a/chromium/content/browser/devtools/renderer_overrides_handler.h b/chromium/content/browser/devtools/renderer_overrides_handler.h index 1d4213d8be6..a5336bf4542 100644 --- a/chromium/content/browser/devtools/renderer_overrides_handler.h +++ b/chromium/content/browser/devtools/renderer_overrides_handler.h @@ -9,10 +9,16 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "cc/output/compositor_frame_metadata.h" #include "content/browser/devtools/devtools_protocol.h" class SkBitmap; +namespace IPC { +class Message; +} + namespace content { class DevToolsAgentHost; @@ -26,27 +32,62 @@ class RendererOverridesHandler : public DevToolsProtocol::Handler { explicit RendererOverridesHandler(DevToolsAgentHost* agent); virtual ~RendererOverridesHandler(); + void OnClientDetached(); + void OnSwapCompositorFrame(const IPC::Message& message); + void OnVisibilityChanged(bool visible); + private: + void InnerSwapCompositorFrame(); + void ParseCaptureParameters(DevToolsProtocol::Command* command, + std::string* format, int* quality, + double* scale); + + // DOM domain. scoped_refptr<DevToolsProtocol::Response> GrantPermissionsForSetFileInputFiles( scoped_refptr<DevToolsProtocol::Command> command); + + // Page domain. + scoped_refptr<DevToolsProtocol::Response> PageDisable( + scoped_refptr<DevToolsProtocol::Command> command); scoped_refptr<DevToolsProtocol::Response> PageHandleJavaScriptDialog( scoped_refptr<DevToolsProtocol::Command> command); scoped_refptr<DevToolsProtocol::Response> PageNavigate( scoped_refptr<DevToolsProtocol::Command> command); + scoped_refptr<DevToolsProtocol::Response> PageReload( + scoped_refptr<DevToolsProtocol::Command> command); + scoped_refptr<DevToolsProtocol::Response> PageGetNavigationHistory( + scoped_refptr<DevToolsProtocol::Command> command); + scoped_refptr<DevToolsProtocol::Response> PageNavigateToHistoryEntry( + scoped_refptr<DevToolsProtocol::Command> command); scoped_refptr<DevToolsProtocol::Response> PageCaptureScreenshot( scoped_refptr<DevToolsProtocol::Command> command); + scoped_refptr<DevToolsProtocol::Response> PageStartScreencast( + scoped_refptr<DevToolsProtocol::Command> command); + scoped_refptr<DevToolsProtocol::Response> PageStopScreencast( + scoped_refptr<DevToolsProtocol::Command> command); void ScreenshotCaptured( scoped_refptr<DevToolsProtocol::Command> command, const std::string& format, int quality, - double scale, + const cc::CompositorFrameMetadata& metadata, bool success, const SkBitmap& bitmap); + void NotifyScreencastVisibility(bool visible); + + // Input domain. + scoped_refptr<DevToolsProtocol::Response> InputDispatchMouseEvent( + scoped_refptr<DevToolsProtocol::Command> command); + scoped_refptr<DevToolsProtocol::Response> InputDispatchGestureEvent( + scoped_refptr<DevToolsProtocol::Command> command); + DevToolsAgentHost* agent_; base::WeakPtrFactory<RendererOverridesHandler> weak_factory_; + scoped_refptr<DevToolsProtocol::Command> screencast_command_; + cc::CompositorFrameMetadata last_compositor_frame_metadata_; + base::TimeTicks last_frame_time_; DISALLOW_COPY_AND_ASSIGN(RendererOverridesHandler); }; diff --git a/chromium/content/browser/devtools/tethering_handler.cc b/chromium/content/browser/devtools/tethering_handler.cc index 711adcd2d72..d3e787d0c01 100644 --- a/chromium/content/browser/devtools/tethering_handler.cc +++ b/chromium/content/browser/devtools/tethering_handler.cc @@ -61,7 +61,7 @@ class SocketPump : public net::StreamListenSocket::Delegate { private: virtual void DidAccept(net::StreamListenSocket* server, - net::StreamListenSocket* socket) OVERRIDE { + scoped_ptr<net::StreamListenSocket> socket) OVERRIDE { if (accepted_socket_.get()) return; @@ -69,7 +69,7 @@ class SocketPump : public net::StreamListenSocket::Delegate { wire_buffer_ = new net::GrowableIOBuffer(); wire_buffer_->SetCapacity(kBufferSize); - accepted_socket_ = socket; + accepted_socket_ = socket.Pass(); int result = client_socket_->Read( buffer_.get(), kBufferSize, @@ -158,8 +158,8 @@ class SocketPump : public net::StreamListenSocket::Delegate { private: scoped_ptr<net::StreamSocket> client_socket_; - scoped_refptr<net::StreamListenSocket> server_socket_; - scoped_refptr<net::StreamListenSocket> accepted_socket_; + scoped_ptr<net::StreamListenSocket> server_socket_; + scoped_ptr<net::StreamListenSocket> accepted_socket_; scoped_refptr<net::IOBuffer> buffer_; scoped_refptr<net::GrowableIOBuffer> wire_buffer_; DevToolsHttpHandlerDelegate* delegate_; diff --git a/chromium/content/browser/devtools/worker_devtools_manager.cc b/chromium/content/browser/devtools/worker_devtools_manager.cc index 14efaa53d41..983c55cfb2b 100644 --- a/chromium/content/browser/devtools/worker_devtools_manager.cc +++ b/chromium/content/browser/devtools/worker_devtools_manager.cc @@ -32,6 +32,16 @@ scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForWorker( worker_route_id); } +// Called on the UI thread. +// static +bool DevToolsAgentHost::HasForWorker( + int worker_process_id, + int worker_route_id) { + return WorkerDevToolsManager::HasDevToolsAgentHostForWorker( + worker_process_id, + worker_route_id); +} + namespace { typedef std::map<WorkerDevToolsManager::WorkerId, @@ -196,6 +206,14 @@ DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker( return it->second; } +// static +bool WorkerDevToolsManager::HasDevToolsAgentHostForWorker( + int worker_process_id, + int worker_route_id) { + WorkerId id(worker_process_id, worker_route_id); + return g_agent_map.Get().find(id) != g_agent_map.Get().end(); +} + WorkerDevToolsManager::WorkerDevToolsManager() { } diff --git a/chromium/content/browser/devtools/worker_devtools_manager.h b/chromium/content/browser/devtools/worker_devtools_manager.h index 0f0111ecf30..e6d090c2159 100644 --- a/chromium/content/browser/devtools/worker_devtools_manager.h +++ b/chromium/content/browser/devtools/worker_devtools_manager.h @@ -32,6 +32,11 @@ class WorkerDevToolsManager { int worker_process_id, int worker_route_id); + // Called on the UI thread. + static bool HasDevToolsAgentHostForWorker( + int worker_process_id, + int worker_route_id); + void ForwardToDevToolsClient(int worker_process_id, int worker_route_id, const std::string& message); diff --git a/chromium/content/browser/dom_storage/dom_storage_browsertest.cc b/chromium/content/browser/dom_storage/dom_storage_browsertest.cc index 54da50a7272..b2aa9f1592b 100644 --- a/chromium/content/browser/dom_storage/dom_storage_browsertest.cc +++ b/chromium/content/browser/dom_storage/dom_storage_browsertest.cc @@ -7,7 +7,7 @@ #include "content/common/dom_storage/dom_storage_types.h" #include "content/public/common/content_paths.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/net_util.h" @@ -25,7 +25,8 @@ class DOMStorageBrowserTest : public ContentBrowserTest { // a #pass or #fail ref. Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell(); NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); - std::string result = the_browser->web_contents()->GetURL().ref(); + std::string result = + the_browser->web_contents()->GetLastCommittedURL().ref(); if (result != "pass") { std::string js_result; ASSERT_TRUE(ExecuteScriptAndExtractString( diff --git a/chromium/content/browser/dom_storage/dom_storage_host.cc b/chromium/content/browser/dom_storage/dom_storage_host.cc index 296d52ec6d7..14d288d8653 100644 --- a/chromium/content/browser/dom_storage/dom_storage_host.cc +++ b/chromium/content/browser/dom_storage/dom_storage_host.cc @@ -30,12 +30,8 @@ bool DOMStorageHost::OpenStorageArea(int connection_id, int namespace_id, return false; // Indicates the renderer gave us very bad data. NamespaceAndArea references; references.namespace_ = context_->GetStorageNamespace(namespace_id); - if (!references.namespace_.get()) { - // TODO(michaeln): Fix crbug/134003 and return false here. - // Until then return true to avoid crashing the renderer for - // sending a bad message. - return true; - } + if (!references.namespace_.get()) + return false; references.area_ = references.namespace_->OpenStorageArea(origin); DCHECK(references.area_.get()); connections_[connection_id] = references; @@ -54,12 +50,8 @@ bool DOMStorageHost::ExtractAreaValues( int connection_id, DOMStorageValuesMap* map) { map->clear(); DOMStorageArea* area = GetOpenArea(connection_id); - if (!area) { - // TODO(michaeln): Fix crbug/134003 and return false here. - // Until then return true to avoid crashing the renderer - // for sending a bad message. - return true; - } + if (!area) + return false; if (!area->IsLoadedInMemory()) { DOMStorageNamespace* ns = GetNamespace(connection_id); DCHECK(ns); @@ -101,12 +93,8 @@ bool DOMStorageHost::SetAreaItem( const base::string16& value, const GURL& page_url, base::NullableString16* old_value) { DOMStorageArea* area = GetOpenArea(connection_id); - if (!area) { - // TODO(michaeln): Fix crbug/134003 and return false here. - // Until then return true to allow the renderer to operate - // to a limited degree out of its cache. - return true; - } + if (!area) + return false; if (!area->SetItem(key, value, old_value)) return false; if (old_value->is_null() || old_value->string() != value) diff --git a/chromium/content/browser/dom_storage/session_storage_database.cc b/chromium/content/browser/dom_storage/session_storage_database.cc index 5fb0c90da3b..bac3df7a67b 100644 --- a/chromium/content/browser/dom_storage/session_storage_database.cc +++ b/chromium/content/browser/dom_storage/session_storage_database.cc @@ -326,7 +326,7 @@ leveldb::Status SessionStorageDatabase::TryToOpen(leveldb::DB** db) { // The directory exists but a valid leveldb database might not exist inside it // (e.g., a subset of the needed files might be missing). Handle this // situation gracefully by creating the database now. - options.max_open_files = 64; // Use minimum. + options.max_open_files = 0; // Use minimum. options.create_if_missing = true; #if defined(OS_WIN) return leveldb::DB::Open(options, WideToUTF8(file_path_.value()), db); diff --git a/chromium/content/browser/dom_storage/session_storage_database_unittest.cc b/chromium/content/browser/dom_storage/session_storage_database_unittest.cc index 9722d72925c..2f86c3b9918 100644 --- a/chromium/content/browser/dom_storage/session_storage_database_unittest.cc +++ b/chromium/content/browser/dom_storage/session_storage_database_unittest.cc @@ -219,7 +219,6 @@ void SessionStorageDatabaseTest::CheckDatabaseConsistency() const { for (DataMap::const_iterator it = data.begin(); it != data.end(); ++it) { std::string namespace_id; - std::string origin; if (IsNamespaceKey(it->first, &namespace_id)) { found_namespace_ids.insert(namespace_id); ++valid_keys; diff --git a/chromium/content/browser/download/base_file_unittest.cc b/chromium/content/browser/download/base_file_unittest.cc index 826f56b4cf8..b82b0a8ab70 100644 --- a/chromium/content/browser/download/base_file_unittest.cc +++ b/chromium/content/browser/download/base_file_unittest.cc @@ -71,7 +71,7 @@ class BaseFileTest : public testing::Test { if (!expected_data_.empty() && !expected_error_) { // Make sure the data has been properly written to disk. std::string disk_data; - EXPECT_TRUE(file_util::ReadFileToString(full_path, &disk_data)); + EXPECT_TRUE(base::ReadFileToString(full_path, &disk_data)); EXPECT_EQ(expected_data_, disk_data); } diff --git a/chromium/content/browser/download/download_browsertest.cc b/chromium/content/browser/download/download_browsertest.cc index 79ad19c9839..767819edbcf 100644 --- a/chromium/content/browser/download/download_browsertest.cc +++ b/chromium/content/browser/download/download_browsertest.cc @@ -22,12 +22,14 @@ #include "content/public/browser/power_save_blocker.h" #include "content/public/common/content_switches.h" #include "content/public/common/webplugininfo.h" +#include "content/public/test/browser_test_utils.h" #include "content/public/test/download_test_observer.h" #include "content/public/test/test_file_error_injector.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" -#include "content/shell/shell_browser_context.h" -#include "content/shell/shell_download_manager_delegate.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_download_manager_delegate.h" +#include "content/shell/browser/shell_network_delegate.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "content/test/net/url_request_mock_http_job.h" @@ -598,7 +600,7 @@ class DownloadContentTest : public ContentBrowserTest { const int64 file_size) { std::string file_contents; - bool read = file_util::ReadFileToString(path, &file_contents); + bool read = base::ReadFileToString(path, &file_contents); EXPECT_TRUE(read) << "Failed reading file: " << path.value() << std::endl; if (!read) return false; // Couldn't read the file. @@ -672,7 +674,7 @@ class DownloadContentTest : public ContentBrowserTest { if (file_exists) { std::string file_contents; - EXPECT_TRUE(file_util::ReadFileToString( + EXPECT_TRUE(base::ReadFileToString( download->GetFullPath(), &file_contents)); ASSERT_EQ(static_cast<size_t>(received_bytes), file_contents.size()); @@ -1598,4 +1600,42 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelResumingDownload) { EXPECT_TRUE(EnsureNoPendingDownloads()); } +// Check that the cookie policy is correctly updated when downloading a file +// that redirects cross origin. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, CookiePolicy) { + ASSERT_TRUE(test_server()->Start()); + net::HostPortPair host_port = test_server()->host_port_pair(); + DCHECK_EQ(host_port.host(), std::string("127.0.0.1")); + + // Block third-party cookies. + ShellNetworkDelegate::SetAcceptAllCookies(false); + + // |url| redirects to a different origin |download| which tries to set a + // cookie. + std::string download(base::StringPrintf( + "http://localhost:%d/set-cookie?A=B", host_port.port())); + GURL url(test_server()->GetURL("server-redirect?" + download)); + + // Download the file. + SetupEnsureNoPendingDownloads(); + scoped_ptr<DownloadUrlParameters> dl_params( + DownloadUrlParameters::FromWebContents(shell()->web_contents(), url)); + scoped_ptr<DownloadTestObserver> observer(CreateWaiter(shell(), 1)); + DownloadManagerForShell(shell())->DownloadUrl(dl_params.Pass()); + observer->WaitForFinished(); + + // Get the important info from other threads and check it. + EXPECT_TRUE(EnsureNoPendingDownloads()); + + std::vector<DownloadItem*> downloads; + DownloadManagerForShell(shell())->GetAllDownloads(&downloads); + ASSERT_EQ(1u, downloads.size()); + ASSERT_EQ(DownloadItem::COMPLETE, downloads[0]->GetState()); + + // Check that the cookies were correctly set. + EXPECT_EQ("A=B", + content::GetCookies(shell()->web_contents()->GetBrowserContext(), + GURL(download))); +} + } // namespace content diff --git a/chromium/content/browser/download/download_file_unittest.cc b/chromium/content/browser/download/download_file_unittest.cc index 867b76f6fef..dcc0e424bd2 100644 --- a/chromium/content/browser/download/download_file_unittest.cc +++ b/chromium/content/browser/download/download_file_unittest.cc @@ -76,6 +76,7 @@ class DownloadFileTest : public testing::Test { DownloadFileTest() : observer_(new StrictMock<MockDownloadDestinationObserver>), observer_factory_(observer_.get()), + input_stream_(NULL), bytes_(-1), bytes_per_sec_(-1), hash_state_("xyzzy"), @@ -163,8 +164,7 @@ class DownloadFileTest : public testing::Test { // Make sure the data has been properly written to disk. std::string disk_data; - EXPECT_TRUE(file_util::ReadFileToString(download_file_->FullPath(), - &disk_data)); + EXPECT_TRUE(base::ReadFileToString(download_file_->FullPath(), &disk_data)); EXPECT_EQ(expected_data_, disk_data); // Make sure the Browser and File threads outlive the DownloadFile @@ -416,7 +416,7 @@ TEST_F(DownloadFileTest, RenameFileFinal) { ASSERT_EQ(static_cast<int>(sizeof(file_data) - 1), file_util::WriteFile(path_5, file_data, sizeof(file_data) - 1)); ASSERT_TRUE(base::PathExists(path_5)); - EXPECT_TRUE(file_util::ReadFileToString(path_5, &file_contents)); + EXPECT_TRUE(base::ReadFileToString(path_5, &file_contents)); EXPECT_EQ(std::string(file_data), file_contents); EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, @@ -424,7 +424,7 @@ TEST_F(DownloadFileTest, RenameFileFinal) { EXPECT_EQ(path_5, output_path); file_contents = ""; - EXPECT_TRUE(file_util::ReadFileToString(path_5, &file_contents)); + EXPECT_TRUE(base::ReadFileToString(path_5, &file_contents)); EXPECT_NE(std::string(file_data), file_contents); DestroyDownloadFile(0); diff --git a/chromium/content/browser/download/download_manager_impl_unittest.cc b/chromium/content/browser/download/download_manager_impl_unittest.cc index eba28339b51..0865c04b2fa 100644 --- a/chromium/content/browser/download/download_manager_impl_unittest.cc +++ b/chromium/content/browser/download/download_manager_impl_unittest.cc @@ -435,7 +435,8 @@ class DownloadManagerTest : public testing::Test { static const size_t kTestDataLen; DownloadManagerTest() - : ui_thread_(BrowserThread::UI, &message_loop_), + : callback_called_(false), + ui_thread_(BrowserThread::UI, &message_loop_), file_thread_(BrowserThread::FILE, &message_loop_), next_download_id_(0) { } diff --git a/chromium/content/browser/download/download_resource_handler.cc b/chromium/content/browser/download/download_resource_handler.cc index 3b4844e6e68..ed4edff67fe 100644 --- a/chromium/content/browser/download/download_resource_handler.cc +++ b/chromium/content/browser/download/download_resource_handler.cc @@ -103,6 +103,9 @@ bool DownloadResourceHandler::OnRequestRedirected( const GURL& url, ResourceResponse* response, bool* defer) { + // We treat a download as a main frame load, and thus update the policy URL + // on redirects. + request_->set_first_party_for_cookies(url); return true; } diff --git a/chromium/content/browser/download/download_resource_handler.h b/chromium/content/browser/download/download_resource_handler.h index d38068d5482..60fbc944387 100644 --- a/chromium/content/browser/download/download_resource_handler.h +++ b/chromium/content/browser/download/download_resource_handler.h @@ -49,7 +49,6 @@ class CONTENT_EXPORT DownloadResourceHandler uint64 position, uint64 size) OVERRIDE; - // Not needed, as this event handler ought to be the final resource. virtual bool OnRequestRedirected(int request_id, const GURL& url, ResourceResponse* response, diff --git a/chromium/content/browser/download/drag_download_file_browsertest.cc b/chromium/content/browser/download/drag_download_file_browsertest.cc index fc35d6c04e0..a4f50056ad3 100644 --- a/chromium/content/browser/download/drag_download_file_browsertest.cc +++ b/chromium/content/browser/download/drag_download_file_browsertest.cc @@ -16,9 +16,9 @@ #include "content/public/common/content_client.h" #include "content/public/test/download_test_observer.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" -#include "content/shell/shell_browser_context.h" -#include "content/shell/shell_download_manager_delegate.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_download_manager_delegate.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "content/test/net/url_request_mock_http_job.h" diff --git a/chromium/content/browser/download/file_metadata_mac.mm b/chromium/content/browser/download/file_metadata_mac.mm index fc0556e0a3b..9dc504fcc49 100644 --- a/chromium/content/browser/download/file_metadata_mac.mm +++ b/chromium/content/browser/download/file_metadata_mac.mm @@ -132,7 +132,7 @@ void AddQuarantineMetadataToFile(const base::FilePath& file, const GURL& source, // need to set the values that the OS can't infer. if (![quarantine_properties valueForKey:(NSString*)kLSQuarantineTypeKey]) { - CFStringRef type = (source.SchemeIs("http") || source.SchemeIs("https")) + CFStringRef type = source.SchemeIsHTTPOrHTTPS() ? kLSQuarantineTypeWebDownload : kLSQuarantineTypeOtherDownload; [quarantine_properties setValue:(NSString*)type diff --git a/chromium/content/browser/download/file_metadata_unittest_linux.cc b/chromium/content/browser/download/file_metadata_unittest_linux.cc index bc8666e076c..4a3ebca5a2c 100644 --- a/chromium/content/browser/download/file_metadata_unittest_linux.cc +++ b/chromium/content/browser/download/file_metadata_unittest_linux.cc @@ -30,7 +30,8 @@ class FileMetadataLinuxTest : public testing::Test { public: FileMetadataLinuxTest() : source_url_("http://www.source.com"), - referrer_url_("http://www.referrer.com") {} + referrer_url_("http://www.referrer.com"), + is_xattr_supported_(false) {} const base::FilePath& test_file() const { return test_file_; diff --git a/chromium/content/browser/download/mhtml_generation_browsertest.cc b/chromium/content/browser/download/mhtml_generation_browsertest.cc index 07bffed6b4b..6e790135bce 100644 --- a/chromium/content/browser/download/mhtml_generation_browsertest.cc +++ b/chromium/content/browser/download/mhtml_generation_browsertest.cc @@ -9,7 +9,7 @@ #include "base/run_loop.h" #include "content/public/browser/web_contents.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -21,7 +21,7 @@ class MHTMLGenerationTest : public ContentBrowserTest { public: MHTMLGenerationTest() : mhtml_generated_(false), file_size_(0) {} - void MHTMLGenerated(const base::FilePath& path, int64 size) { + void MHTMLGenerated(int64 size) { mhtml_generated_ = true; file_size_ = size; base::MessageLoopForUI::current()->Quit(); diff --git a/chromium/content/browser/download/mhtml_generation_manager.cc b/chromium/content/browser/download/mhtml_generation_manager.cc index c4bc378c148..7d7d4bb2645 100644 --- a/chromium/content/browser/download/mhtml_generation_manager.cc +++ b/chromium/content/browser/download/mhtml_generation_manager.cc @@ -36,29 +36,12 @@ MHTMLGenerationManager::MHTMLGenerationManager() { MHTMLGenerationManager::~MHTMLGenerationManager() { } -void MHTMLGenerationManager::GenerateMHTML( - WebContents* web_contents, - const base::FilePath& file, - const GenerateMHTMLCallback& callback) { +void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents, + const base::FilePath& file, + const GenerateMHTMLCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - static int id_counter = 0; - int job_id = id_counter++; - Job job; - job.file_path = file; - job.process_id = web_contents->GetRenderProcessHost()->GetID(); - job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID(); - job.callback = callback; - id_to_job_[job_id] = job; - if (!registrar_.IsRegistered( - this, - NOTIFICATION_RENDERER_PROCESS_TERMINATED, - Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) { - registrar_.Add( - this, - NOTIFICATION_RENDERER_PROCESS_TERMINATED, - Source<RenderProcessHost>(web_contents->GetRenderProcessHost())); - } + int job_id = NewJob(web_contents, callback); base::ProcessHandle renderer_process = web_contents->GetRenderProcessHost()->GetHandle(); @@ -67,6 +50,23 @@ void MHTMLGenerationManager::GenerateMHTML( job_id, file, renderer_process)); } +void MHTMLGenerationManager::StreamMHTML( + WebContents* web_contents, + const base::PlatformFile browser_file, + const GenerateMHTMLCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + int job_id = NewJob(web_contents, callback); + + base::ProcessHandle renderer_process = + web_contents->GetRenderProcessHost()->GetHandle(); + IPC::PlatformFileForTransit renderer_file = + IPC::GetFileHandleForProcess(browser_file, renderer_process, false); + + FileHandleAvailable(job_id, browser_file, renderer_file); +} + + void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) { JobFinished(job_id, mhtml_data_size); } @@ -86,12 +86,17 @@ void MHTMLGenerationManager::CreateFile( IPC::PlatformFileForTransit renderer_file = IPC::GetFileHandleForProcess(browser_file, renderer_process, false); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&MHTMLGenerationManager::FileCreated, base::Unretained(this), - job_id, browser_file, renderer_file)); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&MHTMLGenerationManager::FileHandleAvailable, + base::Unretained(this), + job_id, + browser_file, + renderer_file)); } -void MHTMLGenerationManager::FileCreated(int job_id, +void MHTMLGenerationManager::FileHandleAvailable(int job_id, base::PlatformFile browser_file, IPC::PlatformFileForTransit renderer_file) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -131,7 +136,7 @@ void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) { } Job& job = iter->second; - job.callback.Run(job.file_path, file_size); + job.callback.Run(file_size); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this), @@ -145,6 +150,26 @@ void MHTMLGenerationManager::CloseFile(base::PlatformFile file) { base::ClosePlatformFile(file); } +int MHTMLGenerationManager::NewJob(WebContents* web_contents, + const GenerateMHTMLCallback& callback) { + static int id_counter = 0; + int job_id = id_counter++; + Job& job = id_to_job_[job_id]; + job.process_id = web_contents->GetRenderProcessHost()->GetID(); + job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID(); + job.callback = callback; + if (!registrar_.IsRegistered( + this, + NOTIFICATION_RENDERER_PROCESS_TERMINATED, + Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) { + registrar_.Add( + this, + NOTIFICATION_RENDERER_PROCESS_TERMINATED, + Source<RenderProcessHost>(web_contents->GetRenderProcessHost())); + } + return job_id; +} + void MHTMLGenerationManager::Observe(int type, const NotificationSource& source, const NotificationDetails& details) { diff --git a/chromium/content/browser/download/mhtml_generation_manager.h b/chromium/content/browser/download/mhtml_generation_manager.h index 0c9ec619557..5525d99b812 100644 --- a/chromium/content/browser/download/mhtml_generation_manager.h +++ b/chromium/content/browser/download/mhtml_generation_manager.h @@ -25,14 +25,20 @@ class MHTMLGenerationManager : public NotificationObserver { public: static MHTMLGenerationManager* GetInstance(); - typedef base::Callback<void(const base::FilePath& /* path to the MHTML file */, - int64 /* size of the file */)> GenerateMHTMLCallback; + typedef base::Callback<void(int64 /* size of the file */)> + GenerateMHTMLCallback; // Instructs the render view to generate a MHTML representation of the current // page for |web_contents|. - void GenerateMHTML(WebContents* web_contents, - const base::FilePath& file, - const GenerateMHTMLCallback& callback); + void SaveMHTML(WebContents* web_contents, + const base::FilePath& file, + const GenerateMHTMLCallback& callback); + + // Instructs the render view to generate a MHTML representation of the current + // page for |web_contents|. + void StreamMHTML(WebContents* web_contents, + const base::PlatformFile file, + const GenerateMHTMLCallback& callback); // Notification from the renderer that the MHTML generation finished. // |mhtml_data_size| contains the size in bytes of the generated MHTML data, @@ -46,8 +52,6 @@ class MHTMLGenerationManager : public NotificationObserver { Job(); ~Job(); - base::FilePath file_path; - // The handles to file the MHTML is saved to, for the browser and renderer // processes. base::PlatformFile browser_file; @@ -73,9 +77,9 @@ class MHTMLGenerationManager : public NotificationObserver { // been created. This returns a handle to that file for the browser process // and one for the renderer process. These handles are // kInvalidPlatformFileValue if the file could not be opened. - void FileCreated(int job_id, - base::PlatformFile browser_file, - IPC::PlatformFileForTransit renderer_file); + void FileHandleAvailable(int job_id, + base::PlatformFile browser_file, + IPC::PlatformFileForTransit renderer_file); // Called on the file thread to close the file the MHTML was saved to. void CloseFile(base::PlatformFile file); @@ -85,6 +89,9 @@ class MHTMLGenerationManager : public NotificationObserver { // |mhtml_data_size| is -1 if the MHTML generation failed. void JobFinished(int job_id, int64 mhtml_data_size); + // Creates an register a new job. + int NewJob(WebContents* web_contents, const GenerateMHTMLCallback& callback); + // Implementation of NotificationObserver. virtual void Observe(int type, const NotificationSource& source, diff --git a/chromium/content/browser/download/save_package.cc b/chromium/content/browser/download/save_package.cc index a386a2be0c3..f38f9cdb1e4 100644 --- a/chromium/content/browser/download/save_package.cc +++ b/chromium/content/browser/download/save_package.cc @@ -363,7 +363,7 @@ void SavePackage::InitWithDownloadItem( } } -void SavePackage::OnMHTMLGenerated(const base::FilePath& path, int64 size) { +void SavePackage::OnMHTMLGenerated(int64 size) { if (size <= 0) { Cancel(false); return; @@ -971,11 +971,11 @@ void SavePackage::DoSavingProcess() { // sub-resource's link can be replaced with local file path, which // sub-resource's link need to be replaced with absolute URL which // point to its internet address because it got error when saving its data. - SaveItem* save_item = NULL; + // Start a new SaveItem job if we still have job in waiting queue. if (waiting_item_queue_.size()) { DCHECK(wait_state_ == NET_FILES); - save_item = waiting_item_queue_.front(); + SaveItem* save_item = waiting_item_queue_.front(); if (save_item->save_source() != SaveFileCreateInfo::SAVE_FILE_FROM_DOM) { SaveNextFile(false); } else if (!in_process_count()) { @@ -1209,8 +1209,7 @@ base::FilePath SavePackage::GetSuggestedNameForSaveAs( bool can_save_as_complete, const std::string& contents_mime_type, const std::string& accept_langs) { - base::FilePath name_with_proper_ext = - base::FilePath::FromWStringHack(UTF16ToWideHack(title_)); + base::FilePath name_with_proper_ext = base::FilePath::FromUTF16Unsafe(title_); // If the page's title matches its URL, use the URL. Try to use the last path // component or if there is none, the domain as the file name. @@ -1238,8 +1237,7 @@ base::FilePath SavePackage::GetSuggestedNameForSaveAs( } else { url_path = "dataurl"; } - name_with_proper_ext = - base::FilePath::FromWStringHack(UTF8ToWide(url_path)); + name_with_proper_ext = base::FilePath::FromUTF8Unsafe(url_path); } // Ask user for getting final saving name. diff --git a/chromium/content/browser/download/save_package.h b/chromium/content/browser/download/save_package.h index b53280d7adf..939002e1eee 100644 --- a/chromium/content/browser/download/save_package.h +++ b/chromium/content/browser/download/save_package.h @@ -129,7 +129,7 @@ class CONTENT_EXPORT SavePackage DownloadItemImpl* item); // Callback for WebContents::GenerateMHTML(). - void OnMHTMLGenerated(const base::FilePath& path, int64 size); + void OnMHTMLGenerated(int64 size); // For testing only. SavePackage(WebContents* web_contents, diff --git a/chromium/content/browser/download/save_package_browsertest.cc b/chromium/content/browser/download/save_package_browsertest.cc index cfa63edefb2..391033238cd 100644 --- a/chromium/content/browser/download/save_package_browsertest.cc +++ b/chromium/content/browser/download/save_package_browsertest.cc @@ -4,7 +4,7 @@ #include "base/files/scoped_temp_dir.h" #include "content/browser/download/save_package.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" diff --git a/chromium/content/browser/download/save_package_unittest.cc b/chromium/content/browser/download/save_package_unittest.cc index 9c8f29f46be..c0aa3f8862b 100644 --- a/chromium/content/browser/download/save_package_unittest.cc +++ b/chromium/content/browser/download/save_package_unittest.cc @@ -434,7 +434,7 @@ TEST_F(SavePackageTest, TestGetUrlToBeSavedViewSource) { base::FilePath(kTestDir).Append(file_name)); NavigateAndCommit(view_source_url); EXPECT_EQ(actual_url, GetUrlToBeSaved()); - EXPECT_EQ(view_source_url, contents()->GetURL()); + EXPECT_EQ(view_source_url, contents()->GetLastCommittedURL()); } } // namespace content diff --git a/chromium/content/browser/fileapi/browser_file_system_helper.cc b/chromium/content/browser/fileapi/browser_file_system_helper.cc index 6df6f1f381c..094f35ac304 100644 --- a/chromium/content/browser/fileapi/browser_file_system_helper.cc +++ b/chromium/content/browser/fileapi/browser_file_system_helper.cc @@ -98,27 +98,6 @@ bool FileSystemURLIsValid( return context->GetFileSystemBackend(url.type()) != NULL; } -bool CheckFileSystemPermissionsForProcess( - fileapi::FileSystemContext* context, int process_id, - const fileapi::FileSystemURL& url, int permissions, - base::PlatformFileError* error) { - DCHECK(error); - - if (!FileSystemURLIsValid(context, url)) { - *error = base::PLATFORM_FILE_ERROR_INVALID_URL; - return false; - } - - if (!ChildProcessSecurityPolicyImpl::GetInstance()-> - HasPermissionsForFileSystemFile(process_id, url, permissions)) { - *error = base::PLATFORM_FILE_ERROR_SECURITY; - return false; - } - - *error = base::PLATFORM_FILE_OK; - return true; -} - void SyncGetPlatformPath(fileapi::FileSystemContext* context, int process_id, const GURL& path, diff --git a/chromium/content/browser/fileapi/browser_file_system_helper.h b/chromium/content/browser/fileapi/browser_file_system_helper.h index ba1e0943628..ad44ced5064 100644 --- a/chromium/content/browser/fileapi/browser_file_system_helper.h +++ b/chromium/content/browser/fileapi/browser_file_system_helper.h @@ -32,14 +32,6 @@ CreateFileSystemContext( CONTENT_EXPORT bool FileSystemURLIsValid(fileapi::FileSystemContext* context, const fileapi::FileSystemURL& url); -// Check whether a process has permission to access the file system URL. -CONTENT_EXPORT bool CheckFileSystemPermissionsForProcess( - fileapi::FileSystemContext* context, - int process_id, - const fileapi::FileSystemURL& url, - int permissions, - base::PlatformFileError* error); - // Get the platform path from a file system URL. This needs to be called // on the FILE thread. CONTENT_EXPORT void SyncGetPlatformPath(fileapi::FileSystemContext* context, diff --git a/chromium/content/browser/fileapi/chrome_blob_storage_context.cc b/chromium/content/browser/fileapi/chrome_blob_storage_context.cc index 728b0d58e25..97151a83f67 100644 --- a/chromium/content/browser/fileapi/chrome_blob_storage_context.cc +++ b/chromium/content/browser/fileapi/chrome_blob_storage_context.cc @@ -7,10 +7,10 @@ #include "base/bind.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_storage_context.h" using base::UserDataAdapter; -using webkit_blob::BlobStorageController; +using webkit_blob::BlobStorageContext; namespace content { @@ -40,7 +40,7 @@ ChromeBlobStorageContext* ChromeBlobStorageContext::GetFor( void ChromeBlobStorageContext::InitializeOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - controller_.reset(new BlobStorageController()); + context_.reset(new BlobStorageContext()); } ChromeBlobStorageContext::~ChromeBlobStorageContext() {} diff --git a/chromium/content/browser/fileapi/chrome_blob_storage_context.h b/chromium/content/browser/fileapi/chrome_blob_storage_context.h index 3992e0e4198..641e2353006 100644 --- a/chromium/content/browser/fileapi/chrome_blob_storage_context.h +++ b/chromium/content/browser/fileapi/chrome_blob_storage_context.h @@ -11,7 +11,7 @@ #include "content/common/content_export.h" namespace webkit_blob { -class BlobStorageController; +class BlobStorageContext; } namespace content { @@ -36,8 +36,8 @@ class CONTENT_EXPORT ChromeBlobStorageContext void InitializeOnIOThread(); - webkit_blob::BlobStorageController* controller() const { - return controller_.get(); + webkit_blob::BlobStorageContext* context() const { + return context_.get(); } protected: @@ -51,7 +51,7 @@ class CONTENT_EXPORT ChromeBlobStorageContext void DeleteOnCorrectThread() const; - scoped_ptr<webkit_blob::BlobStorageController> controller_; + scoped_ptr<webkit_blob::BlobStorageContext> context_; }; struct ChromeBlobStorageContextDeleter { diff --git a/chromium/content/browser/fileapi/file_system_browsertest.cc b/chromium/content/browser/fileapi/file_system_browsertest.cc index 7c464a36e9f..eafb1b1cfa4 100644 --- a/chromium/content/browser/fileapi/file_system_browsertest.cc +++ b/chromium/content/browser/fileapi/file_system_browsertest.cc @@ -13,7 +13,7 @@ #include "content/public/browser/storage_partition.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "webkit/browser/quota/quota_manager.h" @@ -36,7 +36,8 @@ class FileSystemBrowserTest : public ContentBrowserTest { LOG(INFO) << "Navigating to URL and blocking."; NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); LOG(INFO) << "Navigation done."; - std::string result = the_browser->web_contents()->GetURL().ref(); + std::string result = + the_browser->web_contents()->GetLastCommittedURL().ref(); if (result != "pass") { std::string js_result; ASSERT_TRUE(ExecuteScriptAndExtractString( diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.cc b/chromium/content/browser/fileapi/fileapi_message_filter.cc index 5641564cff9..1a79ea2d349 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter.cc +++ b/chromium/content/browser/fileapi/fileapi_message_filter.cc @@ -28,7 +28,8 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "url/gurl.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_storage_context.h" +#include "webkit/browser/blob/blob_storage_host.h" #include "webkit/browser/fileapi/file_observers.h" #include "webkit/browser/fileapi/file_permission_policy.h" #include "webkit/browser/fileapi/file_system_context.h" @@ -37,9 +38,15 @@ #include "webkit/common/blob/blob_data.h" #include "webkit/common/blob/shareable_file_reference.h" #include "webkit/common/fileapi/directory_entry.h" +#include "webkit/common/fileapi/file_system_info.h" #include "webkit/common/fileapi/file_system_types.h" #include "webkit/common/fileapi/file_system_util.h" +#if defined(ENABLE_PLUGINS) +#include "content/browser/renderer_host/pepper/pepper_security_helper.h" +#include "ppapi/shared_impl/file_type_conversion.h" +#endif // defined(ENABLE_PLUGINS) + using fileapi::FileSystemFileUtil; using fileapi::FileSystemBackend; using fileapi::FileSystemOperation; @@ -47,7 +54,8 @@ using fileapi::FileSystemURL; using fileapi::FileUpdateObserver; using fileapi::UpdateObserverList; using webkit_blob::BlobData; -using webkit_blob::BlobStorageController; +using webkit_blob::BlobStorageContext; +using webkit_blob::BlobStorageHost; namespace content { @@ -68,6 +76,7 @@ FileAPIMessageFilter::FileAPIMessageFilter( StreamContext* stream_context) : process_id_(process_id), context_(file_system_context), + security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()), request_context_getter_(request_context_getter), request_context_(NULL), blob_storage_context_(blob_storage_context), @@ -86,6 +95,7 @@ FileAPIMessageFilter::FileAPIMessageFilter( StreamContext* stream_context) : process_id_(process_id), context_(file_system_context), + security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()), request_context_(request_context), blob_storage_context_(blob_storage_context), stream_context_(stream_context) { @@ -106,6 +116,9 @@ void FileAPIMessageFilter::OnChannelConnected(int32 peer_pid) { DCHECK(request_context_); } + blob_storage_host_.reset( + new BlobStorageHost(blob_storage_context_->context())); + operation_runner_ = context_->CreateFileSystemOperationRunner(); } @@ -115,10 +128,7 @@ void FileAPIMessageFilter::OnChannelClosing() { // Unregister all the blob and stream URLs that are previously registered in // this process. - for (base::hash_set<std::string>::const_iterator iter = blob_urls_.begin(); - iter != blob_urls_.end(); ++iter) { - blob_storage_context_->controller()->RemoveBlob(GURL(*iter)); - } + blob_storage_host_.reset(); for (base::hash_set<std::string>::const_iterator iter = stream_urls_.begin(); iter != stream_urls_.end(); ++iter) { stream_context_->registry()->UnregisterStream(GURL(*iter)); @@ -159,6 +169,7 @@ bool FileAPIMessageFilter::OnMessageReceived( bool handled = true; IPC_BEGIN_MESSAGE_MAP_EX(FileAPIMessageFilter, message, *message_was_ok) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Open, OnOpen) + IPC_MESSAGE_HANDLER(FileSystemHostMsg_ResolveURL, OnResolveURL) IPC_MESSAGE_HANDLER(FileSystemHostMsg_DeleteFileSystem, OnDeleteFileSystem) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Move, OnMove) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Copy, OnCopy) @@ -168,10 +179,13 @@ bool FileAPIMessageFilter::OnMessageReceived( IPC_MESSAGE_HANDLER(FileSystemHostMsg_Exists, OnExists) IPC_MESSAGE_HANDLER(FileSystemHostMsg_ReadDirectory, OnReadDirectory) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Write, OnWrite) + IPC_MESSAGE_HANDLER(FileSystemHostMsg_WriteDeprecated, OnWriteDeprecated) IPC_MESSAGE_HANDLER(FileSystemHostMsg_Truncate, OnTruncate) IPC_MESSAGE_HANDLER(FileSystemHostMsg_TouchFile, OnTouchFile) IPC_MESSAGE_HANDLER(FileSystemHostMsg_CancelWrite, OnCancel) - IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenFile, OnOpenFile) +#if defined(ENABLE_PLUGINS) + IPC_MESSAGE_HANDLER(FileSystemHostMsg_OpenPepperFile, OnOpenPepperFile) +#endif // defined(ENABLE_PLUGINS) IPC_MESSAGE_HANDLER(FileSystemHostMsg_NotifyCloseFile, OnNotifyCloseFile) IPC_MESSAGE_HANDLER(FileSystemHostMsg_CreateSnapshotFile, OnCreateSnapshotFile) @@ -187,14 +201,26 @@ bool FileAPIMessageFilter::OnMessageReceived( IPC_MESSAGE_HANDLER(BlobHostMsg_SyncAppendSharedMemory, OnAppendSharedMemoryToBlob) IPC_MESSAGE_HANDLER(BlobHostMsg_FinishBuilding, OnFinishBuildingBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_Clone, OnCloneBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_Remove, OnRemoveBlob) + IPC_MESSAGE_HANDLER(BlobHostMsg_IncrementRefCount, + OnIncrementBlobRefCount) + IPC_MESSAGE_HANDLER(BlobHostMsg_DecrementRefCount, + OnDecrementBlobRefCount) + IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL, + OnRegisterPublicBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedRegisterBlobURL, + OnDeprecatedRegisterBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedRevokeBlobURL, + OnDeprecatedRevokeBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_DeprecatedCloneBlobURL, + OnDeprecatedCloneBlobURL) IPC_MESSAGE_HANDLER(StreamHostMsg_StartBuilding, OnStartBuildingStream) IPC_MESSAGE_HANDLER(StreamHostMsg_AppendBlobDataItem, OnAppendBlobDataItemToStream) IPC_MESSAGE_HANDLER(StreamHostMsg_SyncAppendSharedMemory, OnAppendSharedMemoryToStream) IPC_MESSAGE_HANDLER(StreamHostMsg_FinishBuilding, OnFinishBuildingStream) + IPC_MESSAGE_HANDLER(StreamHostMsg_AbortBuilding, OnAbortBuildingStream) IPC_MESSAGE_HANDLER(StreamHostMsg_Clone, OnCloneStream) IPC_MESSAGE_HANDLER(StreamHostMsg_Remove, OnRemoveStream) IPC_MESSAGE_UNHANDLED(handled = false) @@ -226,6 +252,23 @@ void FileAPIMessageFilter::OnOpen( &FileAPIMessageFilter::DidOpenFileSystem, this, request_id)); } +void FileAPIMessageFilter::OnResolveURL( + int request_id, + const GURL& filesystem_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + FileSystemURL url(context_->CrackURL(filesystem_url)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); + return; + } + + context_->ResolveURL(url, base::Bind( + &FileAPIMessageFilter::DidResolveURL, this, request_id)); +} + void FileAPIMessageFilter::OnDeleteFileSystem( int request_id, const GURL& origin_url, @@ -238,15 +281,17 @@ void FileAPIMessageFilter::OnDeleteFileSystem( void FileAPIMessageFilter::OnMove( int request_id, const GURL& src_path, const GURL& dest_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL src_url(context_->CrackURL(src_path)); FileSystemURL dest_url(context_->CrackURL(dest_path)); - const int src_permissions = - fileapi::kReadFilePermissions | fileapi::kWriteFilePermissions; - if (!HasPermissionsForFile(src_url, src_permissions, &error) || - !HasPermissionsForFile( - dest_url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, src_url) || + !ValidateFileSystemURL(request_id, dest_url)) { + return; + } + if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) || + !security_policy_->CanWriteFileSystemFile(process_id_, src_url) || + !security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -258,28 +303,34 @@ void FileAPIMessageFilter::OnMove( void FileAPIMessageFilter::OnCopy( int request_id, const GURL& src_path, const GURL& dest_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL src_url(context_->CrackURL(src_path)); FileSystemURL dest_url(context_->CrackURL(dest_path)); - if (!HasPermissionsForFile(src_url, fileapi::kReadFilePermissions, &error) || - !HasPermissionsForFile( - dest_url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, src_url) || + !ValidateFileSystemURL(request_id, dest_url)) { + return; + } + if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) || + !security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } operations_[request_id] = operation_runner()->Copy( src_url, dest_url, + fileapi::FileSystemOperationRunner::CopyProgressCallback(), base::Bind(&FileAPIMessageFilter::DidFinish, this, request_id)); } void FileAPIMessageFilter::OnRemove( int request_id, const GURL& path, bool recursive) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kWriteFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -291,10 +342,12 @@ void FileAPIMessageFilter::OnRemove( void FileAPIMessageFilter::OnReadMetadata( int request_id, const GURL& path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -306,10 +359,12 @@ void FileAPIMessageFilter::OnCreate( int request_id, const GURL& path, bool exclusive, bool is_directory, bool recursive) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -327,10 +382,12 @@ void FileAPIMessageFilter::OnCreate( void FileAPIMessageFilter::OnExists( int request_id, const GURL& path, bool is_directory) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -348,10 +405,12 @@ void FileAPIMessageFilter::OnExists( void FileAPIMessageFilter::OnReadDirectory( int request_id, const GURL& path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -360,11 +419,21 @@ void FileAPIMessageFilter::OnReadDirectory( this, request_id)); } -void FileAPIMessageFilter::OnWrite( +void FileAPIMessageFilter::OnWriteDeprecated( int request_id, const GURL& path, const GURL& blob_url, int64 offset) { + std::string uuid = + blob_storage_context_->context()->LookupUuidFromDeprecatedURL(blob_url); + OnWrite(request_id, path, uuid, offset); +} + +void FileAPIMessageFilter::OnWrite( + int request_id, + const GURL& path, + const std::string& blob_uuid, + int64 offset) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!request_context_) { // We can't write w/o a request context, trying to do so will crash. @@ -373,14 +442,19 @@ void FileAPIMessageFilter::OnWrite( } FileSystemURL url(context_->CrackURL(path)); - base::PlatformFileError error; - if (!HasPermissionsForFile(url, fileapi::kWriteFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } + scoped_ptr<webkit_blob::BlobDataHandle> blob = + blob_storage_context_->context()->GetBlobDataFromUUID(blob_uuid); + operations_[request_id] = operation_runner()->Write( - request_context_, url, blob_url, offset, + request_context_, url, blob.Pass(), offset, base::Bind(&FileAPIMessageFilter::DidWrite, this, request_id)); } @@ -388,10 +462,12 @@ void FileAPIMessageFilter::OnTruncate( int request_id, const GURL& path, int64 length) { - base::PlatformFileError error; FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, fileapi::kWriteFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -407,9 +483,11 @@ void FileAPIMessageFilter::OnTouchFile( const base::Time& last_modified_time) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); FileSystemURL url(context_->CrackURL(path)); - base::PlatformFileError error; - if (!HasPermissionsForFile(url, fileapi::kCreateFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -437,14 +515,16 @@ void FileAPIMessageFilter::OnCancel( } } -void FileAPIMessageFilter::OnOpenFile( - int request_id, const GURL& path, int file_flags) { +#if defined(ENABLE_PLUGINS) +void FileAPIMessageFilter::OnOpenPepperFile( + int request_id, const GURL& path, int pp_open_flags) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - base::PlatformFileError error; - const int open_permissions = file_flags & fileapi::kOpenPepperFilePermissions; - FileSystemURL url(context_->CrackURL(path)); - if (!HasPermissionsForFile(url, open_permissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); +FileSystemURL url(context_->CrackURL(path)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!CanOpenFileSystemURLWithPepperFlags(pp_open_flags, process_id_, url)) { + Send(new FileSystemMsg_DidFail( + request_id, base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -461,11 +541,19 @@ void FileAPIMessageFilter::OnOpenFile( quota_policy = quota::kQuotaLimitTypeLimited; } + int platform_file_flags = 0; + if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(pp_open_flags, + &platform_file_flags)) { + // |pp_open_flags| should have already been checked in PepperFileIOHost. + NOTREACHED() << "Open file request with invalid pp_open_flags ignored."; + } + operations_[request_id] = operation_runner()->OpenFile( - url, open_permissions, PeerHandle(), + url, platform_file_flags, PeerHandle(), base::Bind(&FileAPIMessageFilter::DidOpenFile, this, request_id, quota_policy)); } +#endif // defined(ENABLE_PLUGINS) void FileAPIMessageFilter::OnNotifyCloseFile(int file_open_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -518,9 +606,11 @@ void FileAPIMessageFilter::OnCreateSnapshotFile( // Make sure if this file can be read by the renderer as this is // called when the renderer is about to create a new File object // (for reading the file). - base::PlatformFileError error; - if (!HasPermissionsForFile(url, fileapi::kReadFilePermissions, &error)) { - Send(new FileSystemMsg_DidFail(request_id, error)); + if (!ValidateFileSystemURL(request_id, url)) + return; + if (!security_policy_->CanReadFileSystemFile(process_id_, url)) { + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_SECURITY)); return; } @@ -535,39 +625,38 @@ void FileAPIMessageFilter::OnDidReceiveSnapshotFile(int request_id) { in_transit_snapshot_files_.erase(request_id); } -void FileAPIMessageFilter::OnStartBuildingBlob(const GURL& url) { +void FileAPIMessageFilter::OnStartBuildingBlob(const std::string& uuid) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->StartBuildingBlob(url); - blob_urls_.insert(url.spec()); + ignore_result(blob_storage_host_->StartBuildingBlob(uuid)); } void FileAPIMessageFilter::OnAppendBlobDataItemToBlob( - const GURL& url, const BlobData::Item& item) { + const std::string& uuid, const BlobData::Item& item) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (item.type() == BlobData::Item::TYPE_FILE_FILESYSTEM) { - base::PlatformFileError error; - FileSystemURL filesystem_url(context_->CrackURL(item.url())); - if (!HasPermissionsForFile(filesystem_url, - fileapi::kReadFilePermissions, &error)) { - OnRemoveBlob(url); + FileSystemURL filesystem_url(context_->CrackURL(item.filesystem_url())); + if (!FileSystemURLIsValid(context_, filesystem_url) || + !security_policy_->CanReadFileSystemFile(process_id_, filesystem_url)) { + ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); return; } } if (item.type() == BlobData::Item::TYPE_FILE && - !ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - process_id_, item.path())) { - OnRemoveBlob(url); + !security_policy_->CanReadFile(process_id_, item.path())) { + ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); return; } if (item.length() == 0) { BadMessageReceived(); return; } - blob_storage_context_->controller()->AppendBlobDataItem(url, item); + ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item)); } void FileAPIMessageFilter::OnAppendSharedMemoryToBlob( - const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size) { + const std::string& uuid, + base::SharedMemoryHandle handle, + size_t buffer_size) { DCHECK(base::SharedMemory::IsHandleValid(handle)); if (!buffer_size) { BadMessageReceived(); @@ -579,33 +668,59 @@ void FileAPIMessageFilter::OnAppendSharedMemoryToBlob( base::SharedMemory shared_memory(handle, true); #endif if (!shared_memory.Map(buffer_size)) { - OnRemoveBlob(url); + ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); return; } BlobData::Item item; item.SetToSharedBytes(static_cast<char*>(shared_memory.memory()), buffer_size); - blob_storage_context_->controller()->AppendBlobDataItem(url, item); + ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item)); } void FileAPIMessageFilter::OnFinishBuildingBlob( - const GURL& url, const std::string& content_type) { + const std::string& uuid, const std::string& content_type) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->FinishBuildingBlob(uuid, content_type)); + // TODO(michaeln): check return values once blink has migrated, crbug/174200 +} + +void FileAPIMessageFilter::OnIncrementBlobRefCount(const std::string& uuid) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->IncrementBlobRefCount(uuid)); +} + +void FileAPIMessageFilter::OnDecrementBlobRefCount(const std::string& uuid) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->DecrementBlobRefCount(uuid)); +} + +void FileAPIMessageFilter::OnRegisterPublicBlobURL( + const GURL& public_url, const std::string& uuid) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->RegisterPublicBlobURL(public_url, uuid)); +} + +void FileAPIMessageFilter::OnRevokePublicBlobURL(const GURL& public_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ignore_result(blob_storage_host_->RevokePublicBlobURL(public_url)); +} + +void FileAPIMessageFilter::OnDeprecatedRegisterBlobURL( + const GURL& url, const std::string& uuid) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->FinishBuildingBlob(url, content_type); + blob_storage_host_->DeprecatedRegisterBlobURL(url, uuid); } -void FileAPIMessageFilter::OnCloneBlob( +void FileAPIMessageFilter::OnDeprecatedCloneBlobURL( const GURL& url, const GURL& src_url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->CloneBlob(url, src_url); - blob_urls_.insert(url.spec()); + blob_storage_host_->DeprecatedCloneBlobURL(url, src_url); } -void FileAPIMessageFilter::OnRemoveBlob(const GURL& url) { +void FileAPIMessageFilter::OnDeprecatedRevokeBlobURL(const GURL& url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - blob_storage_context_->controller()->RemoveBlob(url); - blob_urls_.erase(url.spec()); + blob_storage_host_->DeprecatedRevokeBlobURL(url); } void FileAPIMessageFilter::OnStartBuildingStream( @@ -631,10 +746,10 @@ void FileAPIMessageFilter::OnAppendBlobDataItemToStream( DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); scoped_refptr<Stream> stream(GetStreamForURL(url)); - if (!stream.get()) { - NOTREACHED(); + // Stream instances may be deleted on error. Just abort if there's no Stream + // instance for |url| in the registry. + if (!stream.get()) return; - } // Data for stream is delivered as TYPE_BYTES item. if (item.type() != BlobData::Item::TYPE_BYTES) { @@ -662,10 +777,8 @@ void FileAPIMessageFilter::OnAppendSharedMemoryToStream( } scoped_refptr<Stream> stream(GetStreamForURL(url)); - if (!stream.get()) { - NOTREACHED(); + if (!stream.get()) return; - } stream->AddData(static_cast<char*>(shared_memory.memory()), buffer_size); } @@ -675,17 +788,22 @@ void FileAPIMessageFilter::OnFinishBuildingStream(const GURL& url) { scoped_refptr<Stream> stream(GetStreamForURL(url)); if (stream.get()) stream->Finalize(); - else - NOTREACHED(); +} + +void FileAPIMessageFilter::OnAbortBuildingStream(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + scoped_refptr<Stream> stream(GetStreamForURL(url)); + if (stream.get()) + stream->Abort(); } void FileAPIMessageFilter::OnCloneStream( const GURL& url, const GURL& src_url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!GetStreamForURL(src_url)) { - NOTREACHED(); + // Abort if there's no Stream instance for |src_url| (source Stream which + // we're going to make |url| point to) in the registry. + if (!GetStreamForURL(src_url)) return; - } stream_context_->registry()->CloneStream(url, src_url); stream_urls_.insert(url.spec()); @@ -694,10 +812,8 @@ void FileAPIMessageFilter::OnCloneStream( void FileAPIMessageFilter::OnRemoveStream(const GURL& url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!GetStreamForURL(url).get()) { - NOTREACHED(); + if (!GetStreamForURL(url).get()) return; - } stream_context_->registry()->UnregisterStream(url); stream_urls_.erase(url.spec()); @@ -776,18 +892,35 @@ void FileAPIMessageFilter::DidWrite(int request_id, void FileAPIMessageFilter::DidOpenFileSystem(int request_id, base::PlatformFileError result, - const std::string& name, + const std::string& filesystem_name, const GURL& root) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (result == base::PLATFORM_FILE_OK) { DCHECK(root.is_valid()); - Send(new FileSystemMsg_DidOpenFileSystem(request_id, name, root)); + Send(new FileSystemMsg_DidOpenFileSystem( + request_id, filesystem_name, root)); } else { Send(new FileSystemMsg_DidFail(request_id, result)); } // For OpenFileSystem we do not create a new operation, so no unregister here. } +void FileAPIMessageFilter::DidResolveURL(int request_id, + base::PlatformFileError result, + const fileapi::FileSystemInfo& info, + const base::FilePath& file_path, + bool is_directory) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (result == base::PLATFORM_FILE_OK) { + DCHECK(info.root_url.is_valid()); + Send(new FileSystemMsg_DidResolveURL( + request_id, info, file_path, is_directory)); + } else { + Send(new FileSystemMsg_DidFail(request_id, result)); + } + // For ResolveURL we do not create a new operation, so no unregister here. +} + void FileAPIMessageFilter::DidDeleteFileSystem( int request_id, base::PlatformFileError result) { @@ -805,7 +938,7 @@ void FileAPIMessageFilter::DidCreateSnapshot( base::PlatformFileError result, const base::PlatformFileInfo& info, const base::FilePath& platform_path, - const scoped_refptr<webkit_blob::ShareableFileReference>& snapshot_file) { + const scoped_refptr<webkit_blob::ShareableFileReference>& /* unused */) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); operations_.erase(request_id); @@ -814,17 +947,16 @@ void FileAPIMessageFilter::DidCreateSnapshot( return; } - scoped_refptr<webkit_blob::ShareableFileReference> file_ref = snapshot_file; - if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - process_id_, platform_path)) { + scoped_refptr<webkit_blob::ShareableFileReference> file_ref = + webkit_blob::ShareableFileReference::Get(platform_path); + if (!security_policy_->CanReadFile(process_id_, platform_path)) { // Give per-file read permission to the snapshot file if it hasn't it yet. // In order for the renderer to be able to read the file via File object, // it must be granted per-file read permission for the file's platform // path. By now, it has already been verified that the renderer has // sufficient permissions to read the file, so giving per-file permission // here must be safe. - ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( - process_id_, platform_path); + security_policy_->GrantReadFile(process_id_, platform_path); // Revoke all permissions for the file when the last ref of the file // is dropped. @@ -849,10 +981,13 @@ void FileAPIMessageFilter::DidCreateSnapshot( request_id, info, platform_path)); } -bool FileAPIMessageFilter::HasPermissionsForFile( - const FileSystemURL& url, int permissions, base::PlatformFileError* error) { - return CheckFileSystemPermissionsForProcess(context_, process_id_, url, - permissions, error); +bool FileAPIMessageFilter::ValidateFileSystemURL( + int request_id, const fileapi::FileSystemURL& url) { + if (FileSystemURLIsValid(context_, url)) + return true; + Send(new FileSystemMsg_DidFail(request_id, + base::PLATFORM_FILE_ERROR_INVALID_URL)); + return false; } scoped_refptr<Stream> FileAPIMessageFilter::GetStreamForURL(const GURL& url) { diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.h b/chromium/content/browser/fileapi/fileapi_message_filter.h index 8680f24a998..e9a707f4ac4 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter.h +++ b/chromium/content/browser/fileapi/fileapi_message_filter.h @@ -37,6 +37,7 @@ class FileSystemURL; class FileSystemContext; class FileSystemOperationRunner; struct DirectoryEntry; +struct FileSystemInfo; } namespace net { @@ -45,10 +46,12 @@ class URLRequestContextGetter; } // namespace net namespace webkit_blob { +class BlobStorageHost; class ShareableFileReference; } namespace content { +class ChildProcessSecurityPolicyImpl; class ChromeBlobStorageContext; // TODO(tyoshino): Factor out code except for IPC gluing from @@ -91,6 +94,8 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { fileapi::FileSystemType type, int64 requested_size, bool create); + void OnResolveURL(int request_id, + const GURL& filesystem_url); void OnDeleteFileSystem(int request_id, const GURL& origin_url, fileapi::FileSystemType type); @@ -111,6 +116,11 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { void OnReadDirectory(int request_id, const GURL& path); void OnWrite(int request_id, const GURL& path, + const std::string& blob_uuid, + int64 offset); + void OnWriteDeprecated( + int request_id, + const GURL& path, const GURL& blob_url, int64 offset); void OnTruncate(int request_id, const GURL& path, int64 length); @@ -119,7 +129,9 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { const base::Time& last_access_time, const base::Time& last_modified_time); void OnCancel(int request_id, int request_to_cancel); - void OnOpenFile(int request_id, const GURL& path, int file_flags); +#if defined(ENABLE_PLUGINS) + void OnOpenPepperFile(int request_id, const GURL& path, int pp_open_flags); +#endif // defined(ENABLE_PLUGINS) void OnNotifyCloseFile(int file_open_id); void OnWillUpdate(const GURL& path); void OnDidUpdate(const GURL& path, int64 delta); @@ -131,14 +143,26 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { // Handlers for BlobHostMsg_ family messages. - void OnStartBuildingBlob(const GURL& url); - void OnAppendBlobDataItemToBlob( - const GURL& url, const webkit_blob::BlobData::Item& item); - void OnAppendSharedMemoryToBlob( - const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size); - void OnFinishBuildingBlob(const GURL& url, const std::string& content_type); - void OnCloneBlob(const GURL& url, const GURL& src_url); - void OnRemoveBlob(const GURL& url); + void OnStartBuildingBlob(const std::string& uuid); + void OnAppendBlobDataItemToBlob(const std::string& uuid, + const webkit_blob::BlobData::Item& item); + void OnAppendSharedMemoryToBlob(const std::string& uuid, + base::SharedMemoryHandle handle, + size_t buffer_size); + void OnFinishBuildingBlob(const std::string& uuid, + const std::string& content_type); + void OnCancelBuildingBlob(const std::string& uuid); + void OnIncrementBlobRefCount(const std::string& uuid); + void OnDecrementBlobRefCount(const std::string& uuid); + void OnRegisterPublicBlobURL(const GURL& public_url, const std::string& uuid); + void OnRevokePublicBlobURL(const GURL& public_url); + + // Extra methods to establish a mapping from old-style blobURLs to uuids, + // and to clone them. These won't be here for long, just during a + // transition period. See crbug/174200 + void OnDeprecatedRegisterBlobURL(const GURL& url, const std::string& uuid); + void OnDeprecatedCloneBlobURL(const GURL& url, const GURL& existing_url); + void OnDeprecatedRevokeBlobURL(const GURL& url); // Handlers for StreamHostMsg_ family messages. // @@ -154,6 +178,7 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { void OnAppendSharedMemoryToStream( const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size); void OnFinishBuildingStream(const GURL& url); + void OnAbortBuildingStream(const GURL& url); void OnCloneStream(const GURL& url, const GURL& src_url); void OnRemoveStream(const GURL& url); @@ -179,8 +204,13 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { bool complete); void DidOpenFileSystem(int request_id, base::PlatformFileError result, - const std::string& name, + const std::string& filesystem_name, const GURL& root); + void DidResolveURL(int request_id, + base::PlatformFileError result, + const fileapi::FileSystemInfo& info, + const base::FilePath& file_path, + bool is_directory); void DidDeleteFileSystem(int request_id, base::PlatformFileError result); void DidCreateSnapshot( @@ -191,12 +221,12 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { const base::FilePath& platform_path, const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref); - // Checks renderer's access permissions for single file. - bool HasPermissionsForFile(const fileapi::FileSystemURL& url, - int permissions, - base::PlatformFileError* error); + // Sends a FileSystemMsg_DidFail and returns false if |url| is invalid. + bool ValidateFileSystemURL(int request_id, const fileapi::FileSystemURL& url); - // Retrieves the Stream object for |url| from |stream_context_|. + // Retrieves the Stream object for |url| from |stream_context_|. Returns unset + // scoped_refptr when there's no Stream instance for the given |url| + // registered with stream_context_->registry(). scoped_refptr<Stream> GetStreamForURL(const GURL& url); fileapi::FileSystemOperationRunner* operation_runner() { @@ -206,6 +236,7 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { int process_id_; fileapi::FileSystemContext* context_; + ChildProcessSecurityPolicyImpl* security_policy_; // Keeps map from request_id to OperationID for ongoing operations. // (Primarily for Cancel operation) @@ -222,9 +253,9 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { scoped_ptr<fileapi::FileSystemOperationRunner> operation_runner_; - // Keep track of blob URLs registered in this process. Need to unregister - // all of them when the renderer process dies. - base::hash_set<std::string> blob_urls_; + // Keeps track of blobs used in this process and cleans up + // when the renderer process dies. + scoped_ptr<webkit_blob::BlobStorageHost> blob_storage_host_; // Keep track of stream URLs registered in this process. Need to unregister // all of them when the renderer process dies. diff --git a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc index 06b112ba64d..90e422c7da6 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc +++ b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc @@ -23,7 +23,7 @@ #include "content/public/test/test_browser_thread.h" #include "net/base/io_buffer.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_storage_context.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/mock_file_system_context.h" #include "webkit/common/blob/blob_data.h" @@ -95,34 +95,6 @@ class FileAPIMessageFilterTest : public testing::Test { scoped_refptr<FileAPIMessageFilter> filter_; }; -TEST_F(FileAPIMessageFilterTest, BuildEmptyBlob) { - webkit_blob::BlobStorageController* controller = - blob_storage_context_->controller(); - - const GURL kUrl("blob:foobar"); - const GURL kDifferentUrl("blob:barfoo"); - - EXPECT_EQ(NULL, controller->GetBlobDataFromUrl(kUrl)); - - BlobHostMsg_StartBuilding start_message(kUrl); - EXPECT_TRUE(InvokeOnMessageReceived(start_message)); - - // Blob is still being built. Nothing should be returned. - EXPECT_EQ(NULL, controller->GetBlobDataFromUrl(kUrl)); - - BlobHostMsg_FinishBuilding finish_message(kUrl, kFakeContentType); - EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); - - // Now, Blob is built. - webkit_blob::BlobData* blob_data = controller->GetBlobDataFromUrl(kUrl); - ASSERT_FALSE(blob_data == NULL); - EXPECT_EQ(0U, blob_data->items().size()); - EXPECT_EQ(kFakeContentType, blob_data->content_type()); - - // Nothing should be returned for a URL we didn't use. - EXPECT_TRUE(controller->GetBlobDataFromUrl(kDifferentUrl) == NULL); -} - TEST_F(FileAPIMessageFilterTest, CloseChannelWithInflightRequest) { scoped_refptr<FileAPIMessageFilter> filter( new FileAPIMessageFilter( @@ -190,14 +162,10 @@ TEST_F(FileAPIMessageFilterTest, MultipleFilters) { TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) { StreamRegistry* stream_registry = stream_context_->registry(); - webkit_blob::BlobStorageController* blob_controller = - blob_storage_context_->controller(); - const GURL kUrl(kFakeBlobInternalUrlSpec); const GURL kDifferentUrl("blob:barfoo"); EXPECT_EQ(NULL, stream_registry->GetStream(kUrl).get()); - EXPECT_EQ(NULL, blob_controller->GetBlobDataFromUrl(kUrl)); StreamHostMsg_StartBuilding start_message(kUrl, kFakeContentType); EXPECT_TRUE(InvokeOnMessageReceived(start_message)); @@ -217,9 +185,6 @@ TEST_F(FileAPIMessageFilterTest, BuildEmptyStream) { StreamHostMsg_FinishBuilding finish_message(kUrl); EXPECT_TRUE(InvokeOnMessageReceived(finish_message)); - // Blob controller shouldn't be affected. - EXPECT_EQ(NULL, blob_controller->GetBlobDataFromUrl(kUrl)); - stream = stream_registry->GetStream(kUrl); ASSERT_FALSE(stream.get() == NULL); EXPECT_EQ(Stream::STREAM_EMPTY, diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc index d59ac180aa8..91ebdf56519 100644 --- a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc +++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc @@ -212,17 +212,9 @@ BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance, GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() : xinput_dll_(base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), xinput_available_(GetXInputDllFunctions()) { - // TODO(teravest): http://crbug.com/260187 - if (base::win::GetVersion() > base::win::VERSION_XP) { - directinput_available_ = SUCCEEDED(DirectInput8Create( - GetModuleHandle(NULL), - DIRECTINPUT_VERSION, - IID_IDirectInput8, - reinterpret_cast<void**>(&directinput_interface_), - NULL)); - } else { - directinput_available_ = false; - } + // TODO(teravest): http://crbug.com/260187 for Windows XP. + // TODO(teravest): http://crbug.com/305267 for later versions of windows. + directinput_available_ = false; for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) pad_state_[i].status = DISCONNECTED; } diff --git a/chromium/content/browser/geolocation/core_location_data_provider_mac.h b/chromium/content/browser/geolocation/core_location_data_provider_mac.h deleted file mode 100644 index 29204aa0d8c..00000000000 --- a/chromium/content/browser/geolocation/core_location_data_provider_mac.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2012 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. - -// This file declares a CoreLocation data provider class that allows the -// CoreLocation framework to run on the UI thread, since the Geolocation API's -// providers all live on the IO thread - -#ifndef CONTENT_BROWSER_GEOLOCATION_CORE_LOCATION_DATA_PROVIDER_H_ -#define CONTENT_BROWSER_GEOLOCATION_CORE_LOCATION_DATA_PROVIDER_H_ - -#include "base/mac/scoped_nsobject.h" -#include "base/memory/ref_counted.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/common/geoposition.h" - -#import <Foundation/Foundation.h> - -@class CoreLocationWrapperMac; - -namespace content { -class CoreLocationProviderMac; - -// Data provider class that allows CoreLocation to run in Chrome's UI thread -// while existing on any of Chrome's threads (in this case the IO thread) -class CoreLocationDataProviderMac - : public base::RefCountedThreadSafe<CoreLocationDataProviderMac> { - public: - CoreLocationDataProviderMac(); - - bool StartUpdating(CoreLocationProviderMac* provider); - void StopUpdating(); - - void UpdatePosition(Geoposition* position); - - private: - friend class base::RefCountedThreadSafe<CoreLocationDataProviderMac>; - ~CoreLocationDataProviderMac(); - - // These must execute in BrowserThread::UI - void StartUpdatingTask(); - void StopUpdatingTask(); - // This must execute in the origin thread (IO thread) - void PositionUpdated(Geoposition position); - - // The wrapper class that supplies this class with position data - base::scoped_nsobject<CoreLocationWrapperMac> wrapper_; - // The LocationProvider class that should receive position data - CoreLocationProviderMac* provider_; -}; - -} // namespace content - -#endif // CONTENT_BROWSER_GEOLOCATION_CORE_LOCATION_DATA_PROVIDER_H_ diff --git a/chromium/content/browser/geolocation/core_location_data_provider_mac.mm b/chromium/content/browser/geolocation/core_location_data_provider_mac.mm deleted file mode 100644 index 8b1cf82c28b..00000000000 --- a/chromium/content/browser/geolocation/core_location_data_provider_mac.mm +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright (c) 2012 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. - -// This file contains the class definitions for the CoreLocation data provider -// class and the accompanying Objective C wrapper class. This data provider -// is used to allow the CoreLocation wrapper to run on the UI thread, since -// CLLocationManager's start and stop updating methods must be called from a -// thread with an active NSRunLoop. Currently only the UI thread appears to -// fill that requirement. - -#include "content/browser/geolocation/core_location_data_provider_mac.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "base/time/time.h" -#include "content/browser/geolocation/core_location_provider_mac.h" -#include "content/browser/geolocation/geolocation_provider_impl.h" - -using content::CoreLocationDataProviderMac; -using content::Geoposition; - -// A few required declarations since the CoreLocation headers are not available -// with the Mac OS X 10.5 SDK. -// TODO(jorgevillatoro): Remove these declarations when we build against 10.6 - -// This idea was borrowed from wifi_data_provider_corewlan_mac.mm -typedef double CLLocationDegrees; -typedef double CLLocationAccuracy; -typedef double CLLocationSpeed; -typedef double CLLocationDirection; -typedef double CLLocationDistance; -typedef struct { - CLLocationDegrees latitude; - CLLocationDegrees longitude; -} CLLocationCoordinate2D; - -enum { - kCLErrorLocationUnknown = 0, - kCLErrorDenied -}; - -@interface CLLocationManager : NSObject -+ (BOOL)locationServicesEnabled; -@property(assign) id delegate; -- (void)startUpdatingLocation; -- (void)stopUpdatingLocation; -@end - -@interface CLLocation : NSObject<NSCopying, NSCoding> -@property(readonly) CLLocationCoordinate2D coordinate; -@property(readonly) CLLocationDistance altitude; -@property(readonly) CLLocationAccuracy horizontalAccuracy; -@property(readonly) CLLocationAccuracy verticalAccuracy; -@property(readonly) CLLocationDirection course; -@property(readonly) CLLocationSpeed speed; -@end - -@protocol CLLocationManagerDelegate -- (void)locationManager:(CLLocationManager*)manager - didUpdateToLocation:(CLLocation*)newLocation - fromLocation:(CLLocation*)oldLocation; -- (void)locationManager:(CLLocationManager*)manager - didFailWithError:(NSError*)error; -@end - -// This wrapper class receives CLLocation objects from CoreLocation, converts -// them to Geoposition objects, and passes them on to the data provider class -// Note: This class has some specific threading requirements, inherited from -// CLLocationManager. The location manaager's start and stop updating -// methods must be called from a thread that has an active run loop (which -// seems to only be the UI thread) -@interface CoreLocationWrapperMac : NSObject<CLLocationManagerDelegate> -{ - @private - NSBundle* bundle_; - Class locationManagerClass_; - id locationManager_; - CoreLocationDataProviderMac* dataProvider_; -} - -- (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider; -- (void)dealloc; - -// Can be called from any thread since it does not require an NSRunLoop. However -// it is not threadsafe to receive concurrent calls until after a first -// successful call (to avoid |bundle_| being double initialized) -- (BOOL)locationDataAvailable; - -// These should always be called from BrowserThread::UI -- (void)startLocation; -- (void)stopLocation; - -// These should only be called by CLLocationManager -- (void)locationManager:(CLLocationManager*)manager - didUpdateToLocation:(CLLocation*)newLocation - fromLocation:(CLLocation*)oldLocation; -- (void)locationManager:(CLLocationManager*)manager - didFailWithError:(NSError*)error; -- (BOOL)loadCoreLocationBundle; - -@end - -@implementation CoreLocationWrapperMac - -- (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider { - DCHECK(dataProvider); - dataProvider_ = dataProvider; - self = [super init]; - return self; -} - -- (void)dealloc { - [locationManager_ setDelegate:nil]; - [locationManager_ release]; - [locationManagerClass_ release]; - [bundle_ release]; - [super dealloc]; -} - -// Load the bundle and check to see if location services are enabled -// but don't do anything else -- (BOOL)locationDataAvailable { - return ([self loadCoreLocationBundle] && - [locationManagerClass_ locationServicesEnabled]); -} - -- (void)startLocation { - if ([self locationDataAvailable]) { - if (!locationManager_) { - locationManager_ = [[locationManagerClass_ alloc] init]; - [locationManager_ setDelegate:self]; - } - [locationManager_ startUpdatingLocation]; - } -} - -- (void)stopLocation { - [locationManager_ stopUpdatingLocation]; -} - -- (void)locationManager:(CLLocationManager*)manager - didUpdateToLocation:(CLLocation*)newLocation - fromLocation:(CLLocation*)oldLocation { - Geoposition position; - position.latitude = [newLocation coordinate].latitude; - position.longitude = [newLocation coordinate].longitude; - position.altitude = [newLocation altitude]; - position.accuracy = [newLocation horizontalAccuracy]; - position.altitude_accuracy = [newLocation verticalAccuracy]; - position.speed = [newLocation speed]; - position.heading = [newLocation course]; - position.timestamp = base::Time::Now(); - position.error_code = Geoposition::ERROR_CODE_NONE; - dataProvider_->UpdatePosition(&position); -} - -- (void)locationManager:(CLLocationManager*)manager - didFailWithError:(NSError*)error { - Geoposition position; - switch ([error code]) { - case kCLErrorLocationUnknown: - position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; - break; - case kCLErrorDenied: - position.error_code = Geoposition::ERROR_CODE_PERMISSION_DENIED; - break; - default: - NOTREACHED() << "Unknown CoreLocation error: " << [error code]; - return; - } - dataProvider_->UpdatePosition(&position); -} - -- (BOOL)loadCoreLocationBundle { - if (!bundle_) { - bundle_ = [[NSBundle alloc] - initWithPath:@"/System/Library/Frameworks/CoreLocation.framework"]; - if (!bundle_) { - DLOG(WARNING) << "Couldn't load CoreLocation Framework"; - return NO; - } - - locationManagerClass_ = [bundle_ classNamed:@"CLLocationManager"]; - } - - return YES; -} - -@end - -namespace content { - -CoreLocationDataProviderMac::CoreLocationDataProviderMac() { - if (base::MessageLoop::current() != - GeolocationProviderImpl::GetInstance()->message_loop()) { - NOTREACHED() << "CoreLocation data provider must be created on " - "the Geolocation thread."; - } - provider_ = NULL; - wrapper_.reset([[CoreLocationWrapperMac alloc] initWithDataProvider:this]); -} - -CoreLocationDataProviderMac::~CoreLocationDataProviderMac() { -} - -// Returns true if the CoreLocation wrapper can load the framework and -// location services are enabled. The pointer argument will only be accessed -// in the origin thread. -bool CoreLocationDataProviderMac:: - StartUpdating(CoreLocationProviderMac* provider) { - DCHECK(provider); - DCHECK(!provider_) << "StartUpdating called twice"; - if (![wrapper_ locationDataAvailable]) return false; - provider_ = provider; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&CoreLocationDataProviderMac::StartUpdatingTask, this)); - return true; -} - -// Clears provider_ so that any leftover messages from CoreLocation get ignored -void CoreLocationDataProviderMac::StopUpdating() { - provider_ = NULL; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&CoreLocationDataProviderMac::StopUpdatingTask, this)); -} - -void CoreLocationDataProviderMac::UpdatePosition(Geoposition *position) { - GeolocationProviderImpl::GetInstance()->message_loop()->PostTask( - FROM_HERE, - base::Bind(&CoreLocationDataProviderMac::PositionUpdated, this, - *position)); -} - -// Runs in BrowserThread::UI -void CoreLocationDataProviderMac::StartUpdatingTask() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - [wrapper_ startLocation]; -} - -// Runs in BrowserThread::UI -void CoreLocationDataProviderMac::StopUpdatingTask() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - [wrapper_ stopLocation]; -} - -void CoreLocationDataProviderMac::PositionUpdated(Geoposition position) { - DCHECK(base::MessageLoop::current() == - GeolocationProviderImpl::GetInstance()->message_loop()); - if (provider_) - provider_->SetPosition(&position); -} - -} // namespace content diff --git a/chromium/content/browser/geolocation/core_location_provider_mac.h b/chromium/content/browser/geolocation/core_location_provider_mac.h deleted file mode 100644 index b4186d4ffb8..00000000000 --- a/chromium/content/browser/geolocation/core_location_provider_mac.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2012 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. - -// This file declares a CoreLocation provider that runs on Mac OS X (10.6). -// Public for testing only - for normal usage this header should not be -// required, as location_provider.h declares the needed factory function. - -#ifndef CONTENT_BROWSER_GEOLOCATION_CORE_LOCATION_PROVIDER_MAC_H_ -#define CONTENT_BROWSER_GEOLOCATION_CORE_LOCATION_PROVIDER_MAC_H_ - -#include "content/browser/geolocation/location_provider_base.h" -#include "content/public/common/geoposition.h" - -namespace content { -class CoreLocationDataProviderMac; - -class CoreLocationProviderMac : public LocationProviderBase { - public: - explicit CoreLocationProviderMac(); - virtual ~CoreLocationProviderMac(); - - // LocationProvider - virtual bool StartProvider(bool high_accuracy) OVERRIDE; - virtual void StopProvider() OVERRIDE; - virtual void GetPosition(Geoposition* position) OVERRIDE; - virtual void OnPermissionGranted() OVERRIDE; - - // Receives new positions and calls UpdateListeners - void SetPosition(Geoposition* position); - - private: - bool is_updating_; - CoreLocationDataProviderMac* data_provider_; - Geoposition position_; -}; - -} // namespace content - -#endif // CONTENT_BROWSER_GEOLOCATION_CORE_LOCATION_PROVIDER_MAC_H_ diff --git a/chromium/content/browser/geolocation/core_location_provider_mac.mm b/chromium/content/browser/geolocation/core_location_provider_mac.mm deleted file mode 100644 index 1c3aca1195c..00000000000 --- a/chromium/content/browser/geolocation/core_location_provider_mac.mm +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2012 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/browser/geolocation/core_location_provider_mac.h" - -#include "base/command_line.h" -#include "base/logging.h" -#include "content/browser/geolocation/core_location_data_provider_mac.h" -#include "content/public/common/content_switches.h" - -namespace content { - -CoreLocationProviderMac::CoreLocationProviderMac() - : is_updating_(false) { - data_provider_ = new CoreLocationDataProviderMac(); - data_provider_->AddRef(); -} - -CoreLocationProviderMac::~CoreLocationProviderMac() { - data_provider_->StopUpdating(); - data_provider_->Release(); -} - -bool CoreLocationProviderMac::StartProvider(bool high_accuracy) { - // StartProvider maybe called multiple times. For example, to update the high - // accuracy hint. - // TODO(jknotten): Support high_accuracy hint in underlying data provider. - if (is_updating_) - return true; - - is_updating_ = data_provider_->StartUpdating(this); - return true; -} - -void CoreLocationProviderMac::StopProvider() { - data_provider_->StopUpdating(); - is_updating_ = false; -} - -void CoreLocationProviderMac::GetPosition(Geoposition* position) { - DCHECK(position); - *position = position_; - DCHECK(position->Validate() || - position->error_code != Geoposition::ERROR_CODE_NONE); -} - -void CoreLocationProviderMac::OnPermissionGranted() { -} - -void CoreLocationProviderMac::SetPosition(Geoposition* position) { - DCHECK(position); - position_ = *position; - DCHECK(position->Validate() || - position->error_code != Geoposition::ERROR_CODE_NONE); - - NotifyCallback(position_); -} - -LocationProvider* NewSystemLocationProvider() { - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kExperimentalLocationFeatures)) { - return new CoreLocationProviderMac; - } - return NULL; -} - -} // namespace content diff --git a/chromium/content/browser/geolocation/device_data_provider.h b/chromium/content/browser/geolocation/device_data_provider.h deleted file mode 100644 index d8907354a23..00000000000 --- a/chromium/content/browser/geolocation/device_data_provider.h +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright (c) 2012 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. - -// A device data provider provides data from the device that is used by a -// NetworkLocationProvider to obtain a position fix. This data may be either -// cell radio data or wifi data. For a given type of data, we use a singleton -// instance of the device data provider, which is used by multiple -// NetworkLocationProvider objects. -// -// This file providers DeviceDataProvider, which provides static methods to -// access the singleton instance. The singleton instance uses a private -// implementation to abstract across platforms and also to allow mock providers -// to be used for testing. -// -// This file also provides DeviceDataProviderImplBase, a base class which -// provides commom functionality for the private implementations. -// -// This file also declares the data structures used to represent cell radio data -// and wifi data. - -#ifndef CONTENT_BROWSER_GEOLOCATION_DEVICE_DATA_PROVIDER_H_ -#define CONTENT_BROWSER_GEOLOCATION_DEVICE_DATA_PROVIDER_H_ - -#include <set> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "base/threading/non_thread_safe.h" -#include "content/common/content_export.h" - -namespace content { - -// Wifi data relating to a single access point. -struct CONTENT_EXPORT AccessPointData { - AccessPointData(); - ~AccessPointData(); - - // MAC address, formatted as per MacAddressAsString16. - string16 mac_address; - int radio_signal_strength; // Measured in dBm - int channel; - int signal_to_noise; // Ratio in dB - string16 ssid; // Network identifier -}; - -// This is to allow AccessPointData to be used in std::set. We order -// lexicographically by MAC address. -struct AccessPointDataLess { - bool operator()(const AccessPointData& data1, - const AccessPointData& data2) const { - return data1.mac_address < data2.mac_address; - } -}; - -// All data for wifi. -struct CONTENT_EXPORT WifiData { - WifiData(); - ~WifiData(); - - // Determines whether a new set of WiFi data differs significantly from this. - bool DiffersSignificantly(const WifiData& other) const; - - // Store access points as a set, sorted by MAC address. This allows quick - // comparison of sets for detecting changes and for caching. - typedef std::set<AccessPointData, AccessPointDataLess> AccessPointDataSet; - AccessPointDataSet access_point_data; -}; - -template<typename DataType> -class DeviceDataProvider; - -// This class just exists to work-around MSVC2005 not being able to have a -// template class implement RefCountedThreadSafe -class CONTENT_EXPORT DeviceDataProviderImplBaseHack - : public base::RefCountedThreadSafe<DeviceDataProviderImplBaseHack> { - protected: - friend class base::RefCountedThreadSafe<DeviceDataProviderImplBaseHack>; - virtual ~DeviceDataProviderImplBaseHack() {} -}; - -// See class DeviceDataProvider for the public client API. -// DeviceDataProvider uses containment to hide platform-specific implementation -// details from common code. This class provides common functionality for these -// contained implementation classes. This is a modified pimpl pattern: this -// class needs to be in the public header due to use of templating. -template<typename DataType> -class DeviceDataProviderImplBase : public DeviceDataProviderImplBaseHack { - public: - DeviceDataProviderImplBase() - : container_(NULL), client_loop_(base::MessageLoop::current()) { - DCHECK(client_loop_); - } - - virtual bool StartDataProvider() = 0; - virtual void StopDataProvider() = 0; - virtual bool GetData(DataType* data) = 0; - - // Sets the container of this class, which is of type DeviceDataProvider. - // This is required to pass as a parameter when making the callback to - // listeners. - void SetContainer(DeviceDataProvider<DataType>* container) { - DCHECK(CalledOnClientThread()); - container_ = container; - } - - typedef typename DeviceDataProvider<DataType>::ListenerInterface - ListenerInterface; - void AddListener(ListenerInterface* listener) { - DCHECK(CalledOnClientThread()); - listeners_.insert(listener); - } - bool RemoveListener(ListenerInterface* listener) { - DCHECK(CalledOnClientThread()); - return listeners_.erase(listener) == 1; - } - - bool has_listeners() const { - DCHECK(CalledOnClientThread()); - return !listeners_.empty(); - } - - protected: - virtual ~DeviceDataProviderImplBase() {} - - // Calls DeviceDataUpdateAvailable() on all registered listeners. - typedef std::set<ListenerInterface*> ListenersSet; - void NotifyListeners() { - // Always make the notify callback via a posted task, so we can unwind - // callstack here and make callback without causing client re-entrancy. - client_loop_->PostTask(FROM_HERE, base::Bind( - &DeviceDataProviderImplBase<DataType>::NotifyListenersInClientLoop, - this)); - } - - bool CalledOnClientThread() const { - return base::MessageLoop::current() == this->client_loop_; - } - - base::MessageLoop* client_loop() const { return client_loop_; } - - private: - void NotifyListenersInClientLoop() { - DCHECK(CalledOnClientThread()); - // It's possible that all the listeners (and the container) went away - // whilst this task was pending. This is fine; the loop will be a no-op. - typename ListenersSet::const_iterator iter = listeners_.begin(); - while (iter != listeners_.end()) { - ListenerInterface* listener = *iter; - ++iter; // Advance iter before callback, in case listener unregisters. - listener->DeviceDataUpdateAvailable(container_); - } - } - - DeviceDataProvider<DataType>* container_; - - // Reference to the client's message loop, all callbacks and access to - // the listeners_ member should happen in this context. - base::MessageLoop* client_loop_; - - ListenersSet listeners_; - - DISALLOW_COPY_AND_ASSIGN(DeviceDataProviderImplBase); -}; - -typedef DeviceDataProviderImplBase<WifiData> WifiDataProviderImplBase; - -// A device data provider -// -// We use a singleton instance of this class which is shared by multiple network -// location providers. These location providers access the instance through the -// Register and Unregister methods. -template<typename DataType> -class DeviceDataProvider : public base::NonThreadSafe { - public: - // Interface to be implemented by listeners to a device data provider. - class ListenerInterface { - public: - // Will be called in the context of the thread that called Register(). - virtual void DeviceDataUpdateAvailable( - DeviceDataProvider<DataType>* provider) = 0; - virtual ~ListenerInterface() {} - }; - - // Sets the factory function which will be used by Register to create the - // implementation used by the singleton instance. This factory approach is - // used to abastract accross both platform-specific implementation and to - // inject mock implementations for testing. - typedef DeviceDataProviderImplBase<DataType>* (*ImplFactoryFunction)(void); - static void SetFactory(ImplFactoryFunction factory_function_in) { - factory_function_ = factory_function_in; - } - - static void ResetFactory() { - factory_function_ = DefaultFactoryFunction; - } - - // Adds a listener, which will be called back with DeviceDataUpdateAvailable - // whenever new data is available. Returns the singleton instance. - static DeviceDataProvider* Register(ListenerInterface* listener) { - bool need_to_start_thread = false; - if (!instance_) { - instance_ = new DeviceDataProvider(); - need_to_start_thread = true; - } - DCHECK(instance_); - DCHECK(instance_->CalledOnValidThread()); - instance_->AddListener(listener); - // Start the provider after adding the listener, to avoid any race in - // it receiving an early callback. - if (need_to_start_thread) { - bool started = instance_->StartDataProvider(); - DCHECK(started); - } - return instance_; - } - - // Removes a listener. If this is the last listener, deletes the singleton - // instance. Return value indicates success. - static bool Unregister(ListenerInterface* listener) { - DCHECK(instance_); - DCHECK(instance_->CalledOnValidThread()); - DCHECK(instance_->has_listeners()); - if (!instance_->RemoveListener(listener)) { - return false; - } - if (!instance_->has_listeners()) { - // Must stop the provider (and any implementation threads) before - // destroying to avoid any race conditions in access to the provider in - // the destructor chain. - instance_->StopDataProvider(); - delete instance_; - instance_ = NULL; - } - return true; - } - - // Provides whatever data the provider has, which may be nothing. Return - // value indicates whether this is all the data the provider could ever - // obtain. - bool GetData(DataType* data) { - DCHECK(this->CalledOnValidThread()); - return impl_->GetData(data); - } - - private: - // Private constructor and destructor, callers access singleton through - // Register and Unregister. - DeviceDataProvider() { - DCHECK(factory_function_); - impl_ = (*factory_function_)(); - DCHECK(impl_.get()); - impl_->SetContainer(this); - } - virtual ~DeviceDataProvider() { - DCHECK(impl_.get()); - impl_->SetContainer(NULL); - } - - void AddListener(ListenerInterface* listener) { - impl_->AddListener(listener); - } - - bool RemoveListener(ListenerInterface* listener) { - return impl_->RemoveListener(listener); - } - - bool has_listeners() const { - return impl_->has_listeners(); - } - - bool StartDataProvider() { - return impl_->StartDataProvider(); - } - - void StopDataProvider() { - impl_->StopDataProvider(); - } - - CONTENT_EXPORT static DeviceDataProviderImplBase<DataType>* - DefaultFactoryFunction(); - - // The singleton-like instance of this class. (Not 'true' singleton, as it - // may go through multiple create/destroy/create cycles per process instance, - // e.g. when under test). - CONTENT_EXPORT static DeviceDataProvider* instance_; - - // The factory function used to create the singleton instance. - CONTENT_EXPORT static ImplFactoryFunction factory_function_; - - // The internal implementation. - scoped_refptr<DeviceDataProviderImplBase<DataType> > impl_; - - DISALLOW_COPY_AND_ASSIGN(DeviceDataProvider); -}; - -typedef DeviceDataProvider<WifiData> WifiDataProvider; - -} // namespace content - -#endif // CONTENT_BROWSER_GEOLOCATION_DEVICE_DATA_PROVIDER_H_ diff --git a/chromium/content/browser/geolocation/device_data_provider_unittest.cc b/chromium/content/browser/geolocation/device_data_provider_unittest.cc deleted file mode 100644 index 42a678c8d5e..00000000000 --- a/chromium/content/browser/geolocation/device_data_provider_unittest.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012 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 "base/threading/platform_thread.h" -#include "content/browser/geolocation/device_data_provider.h" -#include "content/browser/geolocation/wifi_data_provider_common.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -class NullWifiDataListenerInterface - : public WifiDataProviderCommon::ListenerInterface { - public: - // ListenerInterface - virtual void DeviceDataUpdateAvailable( - DeviceDataProvider<WifiData>* provider) OVERRIDE {} -}; - -TEST(GeolocationDeviceDataProviderWifiData, CreateDestroy) { - // See http://crbug.com/59913 . The main_message_loop is not required to be - // run for correct behaviour, but we run it in this test to help smoke out - // any race conditions between processing in the main loop and the setup / - // tear down of the DeviceDataProvider thread. - base::MessageLoopForUI main_message_loop; - NullWifiDataListenerInterface listener; - for (int i = 0; i < 10; i++) { - DeviceDataProvider<WifiData>::Register(&listener); - for (int j = 0; j < 10; j++) { - base::PlatformThread::Sleep(base::TimeDelta()); - main_message_loop.RunUntilIdle(); // See comment above - } - DeviceDataProvider<WifiData>::Unregister(&listener); - for (int j = 0; j < 10; j++) { - base::PlatformThread::Sleep(base::TimeDelta()); - main_message_loop.RunUntilIdle(); // See comment above - } - } -} - -} // namespace content diff --git a/chromium/content/browser/geolocation/empty_device_data_provider.cc b/chromium/content/browser/geolocation/empty_device_data_provider.cc deleted file mode 100644 index 57e25b0a504..00000000000 --- a/chromium/content/browser/geolocation/empty_device_data_provider.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2012 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/browser/geolocation/empty_device_data_provider.h" - -namespace content { - -// Only define for platforms that lack a real wifi data provider. -#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX) -// static -template<> -WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { - return new EmptyDeviceDataProvider<WifiData>(); -} -#endif - -} // namespace content diff --git a/chromium/content/browser/geolocation/empty_device_data_provider.h b/chromium/content/browser/geolocation/empty_device_data_provider.h deleted file mode 100644 index 76653f55cfe..00000000000 --- a/chromium/content/browser/geolocation/empty_device_data_provider.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012 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_BROWSER_GEOLOCATION_EMPTY_DEVICE_DATA_PROVIDER_H_ -#define CONTENT_BROWSER_GEOLOCATION_EMPTY_DEVICE_DATA_PROVIDER_H_ - -#include "content/browser/geolocation/device_data_provider.h" - -namespace content { - -// An implementation of DeviceDataProviderImplBase that does not provide any -// data. Used on platforms where a given data type is not available. - -template<typename DataType> -class EmptyDeviceDataProvider : public DeviceDataProviderImplBase<DataType> { - public: - EmptyDeviceDataProvider() {} - virtual ~EmptyDeviceDataProvider() {} - - // DeviceDataProviderImplBase implementation - virtual bool StartDataProvider() { return true; } - virtual void StopDataProvider() { } - virtual bool GetData(DataType *data) { - DCHECK(data); - // This is all the data we can get - nothing. - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(EmptyDeviceDataProvider); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_GEOLOCATION_EMPTY_DEVICE_DATA_PROVIDER_H_ diff --git a/chromium/content/browser/geolocation/empty_wifi_data_provider.cc b/chromium/content/browser/geolocation/empty_wifi_data_provider.cc new file mode 100644 index 00000000000..7255f46fcc9 --- /dev/null +++ b/chromium/content/browser/geolocation/empty_wifi_data_provider.cc @@ -0,0 +1,29 @@ +// Copyright 2013 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/browser/geolocation/empty_wifi_data_provider.h" + +namespace content { + +EmptyWifiDataProvider::EmptyWifiDataProvider() { +} + +EmptyWifiDataProvider::~EmptyWifiDataProvider() { +} + +bool EmptyWifiDataProvider::GetData(WifiData* data) { + DCHECK(data); + // This is all the data we can get - nothing. + return true; +} + +// Only define for platforms that lack a real wifi data provider. +#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX) +// static +WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { + return new EmptyWifiDataProvider(); +} +#endif + +} // namespace content diff --git a/chromium/content/browser/geolocation/empty_wifi_data_provider.h b/chromium/content/browser/geolocation/empty_wifi_data_provider.h new file mode 100644 index 00000000000..a02ed2bc9b7 --- /dev/null +++ b/chromium/content/browser/geolocation/empty_wifi_data_provider.h @@ -0,0 +1,31 @@ +// Copyright 2013 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_BROWSER_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_ +#define CONTENT_BROWSER_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_ + +#include "content/browser/geolocation/wifi_data_provider.h" + +namespace content { + +// An implementation of WifiDataProviderImplBase that does not provide any +// data. Used on platforms where a real implementation is not available. +class EmptyWifiDataProvider : public WifiDataProviderImplBase { + public: + EmptyWifiDataProvider(); + + // WifiDataProviderImplBase implementation + virtual void StartDataProvider() OVERRIDE { } + virtual void StopDataProvider() OVERRIDE { } + virtual bool GetData(WifiData* data) OVERRIDE; + + private: + virtual ~EmptyWifiDataProvider(); + + DISALLOW_COPY_AND_ASSIGN(EmptyWifiDataProvider); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_ diff --git a/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc b/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc index 53f8e4feafb..38f77504cfb 100644 --- a/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc +++ b/chromium/content/browser/geolocation/geolocation_dispatcher_host.cc @@ -20,7 +20,7 @@ namespace content { namespace { -void NotifyArbitratorPermissionGranted() { +void NotifyGeolocationProviderPermissionGranted() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); GeolocationProviderImpl::GetInstance()->UserDidOptIntoLocationServices(); } @@ -30,16 +30,17 @@ void SendGeolocationPermissionResponse(int render_process_id, int bridge_id, bool allowed) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - RenderViewHostImpl* r = + RenderViewHostImpl* render_view_host = RenderViewHostImpl::FromID(render_process_id, render_view_id); - if (!r) + if (!render_view_host) return; - r->Send(new GeolocationMsg_PermissionSet(render_view_id, bridge_id, allowed)); + render_view_host->Send( + new GeolocationMsg_PermissionSet(render_view_id, bridge_id, allowed)); if (allowed) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&NotifyArbitratorPermissionGranted)); + base::Bind(&NotifyGeolocationProviderPermissionGranted)); } } @@ -67,7 +68,7 @@ class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost { bool enable_high_accuracy); void OnStopUpdating(int render_view_id); - // Updates the |location_arbitrator_| with the currently required update + // Updates the |geolocation_provider_| with the currently required update // options, based on |renderer_high_accuracy_|. void RefreshHighAccuracy(); @@ -81,11 +82,11 @@ class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost { // context switches. // Only used on the IO thread. std::set<int> geolocation_renderer_ids_; - // Maps renderer_id to whether high accuracy is requestd for this particular + // Maps renderer_id to whether high accuracy is requested for this particular // bridge. std::map<int, bool> renderer_high_accuracy_; - // Only set whilst we are registered with the arbitrator. - GeolocationProviderImpl* location_provider_; + // Only set whilst we are registered with the geolocation provider. + GeolocationProviderImpl* geolocation_provider_; GeolocationProviderImpl::LocationUpdateCallback callback_; @@ -97,7 +98,7 @@ GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl( GeolocationPermissionContext* geolocation_permission_context) : render_process_id_(render_process_id), geolocation_permission_context_(geolocation_permission_context), - location_provider_(NULL) { + geolocation_provider_(NULL) { callback_ = base::Bind( &GeolocationDispatcherHostImpl::OnLocationUpdate, base::Unretained(this)); // This is initialized by ResourceMessageFilter. Do not add any non-trivial @@ -106,8 +107,8 @@ GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl( } GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() { - if (location_provider_) - location_provider_->RemoveLocationUpdateCallback(callback_); + if (geolocation_provider_) + geolocation_provider_->RemoveLocationUpdateCallback(callback_); } bool GeolocationDispatcherHostImpl::OnMessageReceived( @@ -204,13 +205,13 @@ void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id) { void GeolocationDispatcherHostImpl::RefreshHighAccuracy() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (renderer_high_accuracy_.empty()) { - if (location_provider_) { - location_provider_->RemoveLocationUpdateCallback(callback_); - location_provider_ = NULL; + if (geolocation_provider_) { + geolocation_provider_->RemoveLocationUpdateCallback(callback_); + geolocation_provider_ = NULL; } } else { - if (!location_provider_) - location_provider_ = GeolocationProviderImpl::GetInstance(); + if (!geolocation_provider_) + geolocation_provider_ = GeolocationProviderImpl::GetInstance(); // Re-add to re-establish our options, in case they changed. bool use_high_accuracy = false; std::map<int, bool>::iterator i = renderer_high_accuracy_.begin(); @@ -220,7 +221,8 @@ void GeolocationDispatcherHostImpl::RefreshHighAccuracy() { break; } } - location_provider_->AddLocationUpdateCallback(callback_, use_high_accuracy); + geolocation_provider_->AddLocationUpdateCallback( + callback_, use_high_accuracy); } } } // namespace diff --git a/chromium/content/browser/geolocation/geolocation_provider_impl.cc b/chromium/content/browser/geolocation/geolocation_provider_impl.cc index f24cb2dcb7a..f343c2920ff 100644 --- a/chromium/content/browser/geolocation/geolocation_provider_impl.cc +++ b/chromium/content/browser/geolocation/geolocation_provider_impl.cc @@ -227,10 +227,10 @@ void GeolocationProviderImpl::CleanUp() { arbitrator_ = NULL; } -GeolocationArbitrator* GeolocationProviderImpl::CreateArbitrator() { - GeolocationArbitratorImpl::LocationUpdateCallback callback = base::Bind( +LocationArbitrator* GeolocationProviderImpl::CreateArbitrator() { + LocationArbitratorImpl::LocationUpdateCallback callback = base::Bind( &GeolocationProviderImpl::OnLocationUpdate, base::Unretained(this)); - return new GeolocationArbitratorImpl(callback); + return new LocationArbitratorImpl(callback); } } // namespace content diff --git a/chromium/content/browser/geolocation/geolocation_provider_impl.h b/chromium/content/browser/geolocation/geolocation_provider_impl.h index d4b5afb1074..6613f44fc69 100644 --- a/chromium/content/browser/geolocation/geolocation_provider_impl.h +++ b/chromium/content/browser/geolocation/geolocation_provider_impl.h @@ -19,17 +19,13 @@ template<typename Type> struct DefaultSingletonTraits; namespace content { -class GeolocationArbitrator; -class GeolocationProviderTest; +class LocationArbitrator; // This is the main API to the geolocation subsystem. The application will hold // a single instance of this class and can register multiple clients to be // notified of location changes: -// * Observers are registered by AddLocationUpdateCallback() and will keep -// receiving updates -// until unregistered by RemoveLocationUpdateCallback(). -// * Callbacks are registered by RequestCallback() and will be called exactly -// once when the next update becomes available. +// * Callbacks are registered by AddLocationUpdateCallback() and will keep +// receiving updates until unregistered by RemoveLocationUpdateCallback(). // The application must instantiate the GeolocationProvider on the IO thread and // must communicate with it on the same thread. // The underlying location arbitrator will only be enabled whilst there is at @@ -53,7 +49,7 @@ class CONTENT_EXPORT GeolocationProviderImpl // position to all registered clients. void OverrideLocationForTesting(const Geoposition& override_position); - // Callback from the GeolocationArbitrator. Public for testing. + // Callback from the LocationArbitrator. Public for testing. void OnLocationUpdate(const Geoposition& position); // Gets a pointer to the singleton instance of the location relayer, which @@ -68,7 +64,7 @@ class CONTENT_EXPORT GeolocationProviderImpl virtual ~GeolocationProviderImpl(); // Useful for injecting mock geolocation arbitrator in tests. - virtual GeolocationArbitrator* CreateArbitrator(); + virtual LocationArbitrator* CreateArbitrator(); private: typedef std::pair<LocationUpdateCallback, bool> LocationUpdateInfo; @@ -107,7 +103,7 @@ class CONTENT_EXPORT GeolocationProviderImpl bool ignore_location_updates_; // Only to be used on the geolocation thread. - GeolocationArbitrator* arbitrator_; + LocationArbitrator* arbitrator_; DISALLOW_COPY_AND_ASSIGN(GeolocationProviderImpl); }; diff --git a/chromium/content/browser/geolocation/geolocation_provider_unittest.cc b/chromium/content/browser/geolocation/geolocation_provider_unittest.cc index 47b397a9a90..a2aa88d9e65 100644 --- a/chromium/content/browser/geolocation/geolocation_provider_unittest.cc +++ b/chromium/content/browser/geolocation/geolocation_provider_unittest.cc @@ -30,21 +30,21 @@ class LocationProviderForTestArbitrator : public GeolocationProviderImpl { virtual ~LocationProviderForTestArbitrator() {} // Only valid for use on the geolocation thread. - MockGeolocationArbitrator* mock_arbitrator() const { + MockLocationArbitrator* mock_arbitrator() const { return mock_arbitrator_; } protected: // GeolocationProviderImpl implementation: - virtual GeolocationArbitrator* CreateArbitrator() OVERRIDE; + virtual LocationArbitrator* CreateArbitrator() OVERRIDE; private: - MockGeolocationArbitrator* mock_arbitrator_; + MockLocationArbitrator* mock_arbitrator_; }; -GeolocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() { +LocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() { DCHECK(mock_arbitrator_ == NULL); - mock_arbitrator_ = new MockGeolocationArbitrator; + mock_arbitrator_ = new MockLocationArbitrator; return mock_arbitrator_; } diff --git a/chromium/content/browser/geolocation/location_arbitrator.h b/chromium/content/browser/geolocation/location_arbitrator.h index 4bf82b03ac5..d2cd191c798 100644 --- a/chromium/content/browser/geolocation/location_arbitrator.h +++ b/chromium/content/browser/geolocation/location_arbitrator.h @@ -12,9 +12,9 @@ namespace content { // This class is responsible for handling updates from multiple underlying // providers and resolving them to a single 'best' location fix at any given // moment. -class CONTENT_EXPORT GeolocationArbitrator { +class CONTENT_EXPORT LocationArbitrator { public: - virtual ~GeolocationArbitrator() {}; + virtual ~LocationArbitrator() {}; // See more details in geolocation_provider. virtual void StartProviders(bool use_high_accuracy) = 0; diff --git a/chromium/content/browser/geolocation/location_arbitrator_impl.cc b/chromium/content/browser/geolocation/location_arbitrator_impl.cc index 49c1c10b85e..149eb133b8e 100644 --- a/chromium/content/browser/geolocation/location_arbitrator_impl.cc +++ b/chromium/content/browser/geolocation/location_arbitrator_impl.cc @@ -23,28 +23,28 @@ const char* kDefaultNetworkProviderUrl = // To avoid oscillations, set this to twice the expected update interval of a // a GPS-type location provider (in case it misses a beat) plus a little. -const int64 GeolocationArbitratorImpl::kFixStaleTimeoutMilliseconds = +const int64 LocationArbitratorImpl::kFixStaleTimeoutMilliseconds = 11 * base::Time::kMillisecondsPerSecond; -GeolocationArbitratorImpl::GeolocationArbitratorImpl( +LocationArbitratorImpl::LocationArbitratorImpl( const LocationUpdateCallback& callback) : callback_(callback), provider_callback_( - base::Bind(&GeolocationArbitratorImpl::LocationUpdateAvailable, + base::Bind(&LocationArbitratorImpl::LocationUpdateAvailable, base::Unretained(this))), position_provider_(NULL), is_permission_granted_(false), is_running_(false) { } -GeolocationArbitratorImpl::~GeolocationArbitratorImpl() { +LocationArbitratorImpl::~LocationArbitratorImpl() { } -GURL GeolocationArbitratorImpl::DefaultNetworkProviderURL() { +GURL LocationArbitratorImpl::DefaultNetworkProviderURL() { return GURL(kDefaultNetworkProviderUrl); } -void GeolocationArbitratorImpl::OnPermissionGranted() { +void LocationArbitratorImpl::OnPermissionGranted() { is_permission_granted_ = true; for (ScopedVector<LocationProvider>::iterator i = providers_.begin(); i != providers_.end(); ++i) { @@ -52,28 +52,28 @@ void GeolocationArbitratorImpl::OnPermissionGranted() { } } -void GeolocationArbitratorImpl::StartProviders(bool use_high_accuracy) { +void LocationArbitratorImpl::StartProviders(bool use_high_accuracy) { // Stash options as OnAccessTokenStoresLoaded has not yet been called. is_running_ = true; use_high_accuracy_ = use_high_accuracy; if (providers_.empty()) { DCHECK(DefaultNetworkProviderURL().is_valid()); GetAccessTokenStore()->LoadAccessTokens( - base::Bind(&GeolocationArbitratorImpl::OnAccessTokenStoresLoaded, + base::Bind(&LocationArbitratorImpl::OnAccessTokenStoresLoaded, base::Unretained(this))); } else { DoStartProviders(); } } -void GeolocationArbitratorImpl::DoStartProviders() { +void LocationArbitratorImpl::DoStartProviders() { for (ScopedVector<LocationProvider>::iterator i = providers_.begin(); i != providers_.end(); ++i) { (*i)->StartProvider(use_high_accuracy_); } } -void GeolocationArbitratorImpl::StopProviders() { +void LocationArbitratorImpl::StopProviders() { // Reset the reference location state (provider+position) // so that future starts use fresh locations from // the newly constructed providers. @@ -84,7 +84,7 @@ void GeolocationArbitratorImpl::StopProviders() { is_running_ = false; } -void GeolocationArbitratorImpl::OnAccessTokenStoresLoaded( +void LocationArbitratorImpl::OnAccessTokenStoresLoaded( AccessTokenStore::AccessTokenSet access_token_set, net::URLRequestContextGetter* context_getter) { if (!is_running_ || !providers_.empty()) { @@ -112,7 +112,7 @@ void GeolocationArbitratorImpl::OnAccessTokenStoresLoaded( DoStartProviders(); } -void GeolocationArbitratorImpl::RegisterProvider( +void LocationArbitratorImpl::RegisterProvider( LocationProvider* provider) { if (!provider) return; @@ -122,7 +122,7 @@ void GeolocationArbitratorImpl::RegisterProvider( providers_.push_back(provider); } -void GeolocationArbitratorImpl::LocationUpdateAvailable( +void LocationArbitratorImpl::LocationUpdateAvailable( const LocationProvider* provider, const Geoposition& new_position) { DCHECK(new_position.Validate() || @@ -135,17 +135,17 @@ void GeolocationArbitratorImpl::LocationUpdateAvailable( callback_.Run(position_); } -AccessTokenStore* GeolocationArbitratorImpl::NewAccessTokenStore() { +AccessTokenStore* LocationArbitratorImpl::NewAccessTokenStore() { return GetContentClient()->browser()->CreateAccessTokenStore(); } -AccessTokenStore* GeolocationArbitratorImpl::GetAccessTokenStore() { +AccessTokenStore* LocationArbitratorImpl::GetAccessTokenStore() { if (!access_token_store_.get()) access_token_store_ = NewAccessTokenStore(); return access_token_store_.get(); } -LocationProvider* GeolocationArbitratorImpl::NewNetworkLocationProvider( +LocationProvider* LocationArbitratorImpl::NewNetworkLocationProvider( AccessTokenStore* access_token_store, net::URLRequestContextGetter* context, const GURL& url, @@ -159,19 +159,19 @@ LocationProvider* GeolocationArbitratorImpl::NewNetworkLocationProvider( #endif } -LocationProvider* GeolocationArbitratorImpl::NewSystemLocationProvider() { -#if defined(OS_WIN) +LocationProvider* LocationArbitratorImpl::NewSystemLocationProvider() { +#if defined(OS_WIN) || defined(OS_MACOSX) return NULL; #else return content::NewSystemLocationProvider(); #endif } -base::Time GeolocationArbitratorImpl::GetTimeNow() const { +base::Time LocationArbitratorImpl::GetTimeNow() const { return base::Time::Now(); } -bool GeolocationArbitratorImpl::IsNewPositionBetter( +bool LocationArbitratorImpl::IsNewPositionBetter( const Geoposition& old_position, const Geoposition& new_position, bool from_same_provider) const { // Updates location_info if it's better than what we currently have, @@ -197,7 +197,7 @@ bool GeolocationArbitratorImpl::IsNewPositionBetter( return false; } -bool GeolocationArbitratorImpl::HasPermissionBeenGranted() const { +bool LocationArbitratorImpl::HasPermissionBeenGranted() const { return is_permission_granted_; } diff --git a/chromium/content/browser/geolocation/location_arbitrator_impl.h b/chromium/content/browser/geolocation/location_arbitrator_impl.h index 91ffa0c356f..45910552e4e 100644 --- a/chromium/content/browser/geolocation/location_arbitrator_impl.h +++ b/chromium/content/browser/geolocation/location_arbitrator_impl.h @@ -27,8 +27,7 @@ class LocationProvider; // This class is responsible for handling updates from multiple underlying // providers and resolving them to a single 'best' location fix at any given // moment. -class CONTENT_EXPORT GeolocationArbitratorImpl - : public GeolocationArbitrator { +class CONTENT_EXPORT LocationArbitratorImpl : public LocationArbitrator { public: // Number of milliseconds newer a location provider has to be that it's worth // switching to this location provider on the basis of it being fresher @@ -37,12 +36,12 @@ class CONTENT_EXPORT GeolocationArbitratorImpl typedef base::Callback<void(const Geoposition&)> LocationUpdateCallback; - explicit GeolocationArbitratorImpl(const LocationUpdateCallback& callback); - virtual ~GeolocationArbitratorImpl(); + explicit LocationArbitratorImpl(const LocationUpdateCallback& callback); + virtual ~LocationArbitratorImpl(); static GURL DefaultNetworkProviderURL(); - // GeolocationArbitrator + // LocationArbitrator virtual void StartProviders(bool use_high_accuracy) OVERRIDE; virtual void StopProviders() OVERRIDE; virtual void OnPermissionGranted() OVERRIDE; @@ -96,7 +95,7 @@ class CONTENT_EXPORT GeolocationArbitratorImpl // Tracks whether providers should be running. bool is_running_; - DISALLOW_COPY_AND_ASSIGN(GeolocationArbitratorImpl); + DISALLOW_COPY_AND_ASSIGN(LocationArbitratorImpl); }; // Factory functions for the various types of location provider to abstract diff --git a/chromium/content/browser/geolocation/location_arbitrator_impl_unittest.cc b/chromium/content/browser/geolocation/location_arbitrator_impl_unittest.cc index ce2d1e27ad0..02df43172ee 100644 --- a/chromium/content/browser/geolocation/location_arbitrator_impl_unittest.cc +++ b/chromium/content/browser/geolocation/location_arbitrator_impl_unittest.cc @@ -62,12 +62,12 @@ void SetReferencePosition(MockLocationProvider* provider) { namespace { -class TestingGeolocationArbitrator : public GeolocationArbitratorImpl { +class TestingLocationArbitrator : public LocationArbitratorImpl { public: - TestingGeolocationArbitrator( - const GeolocationArbitratorImpl::LocationUpdateCallback& callback, + TestingLocationArbitrator( + const LocationArbitratorImpl::LocationUpdateCallback& callback, AccessTokenStore* access_token_store) - : GeolocationArbitratorImpl(callback), + : LocationArbitratorImpl(callback), cell_(NULL), gps_(NULL), access_token_store_(access_token_store) { @@ -110,10 +110,10 @@ class GeolocationLocationArbitratorTest : public testing::Test { virtual void SetUp() { access_token_store_ = new NiceMock<FakeAccessTokenStore>; observer_.reset(new MockLocationObserver); - GeolocationArbitratorImpl::LocationUpdateCallback callback = + LocationArbitratorImpl::LocationUpdateCallback callback = base::Bind(&MockLocationObserver::OnLocationUpdate, base::Unretained(observer_.get())); - arbitrator_.reset(new TestingGeolocationArbitrator( + arbitrator_.reset(new TestingLocationArbitrator( callback, access_token_store_.get())); } @@ -134,7 +134,7 @@ class GeolocationLocationArbitratorTest : public testing::Test { base::TimeDelta SwitchOnFreshnessCliff() { // Add 1, to ensure it meets any greater-than test. return base::TimeDelta::FromMilliseconds( - GeolocationArbitratorImpl::kFixStaleTimeoutMilliseconds + 1); + LocationArbitratorImpl::kFixStaleTimeoutMilliseconds + 1); } MockLocationProvider* cell() { @@ -147,7 +147,7 @@ class GeolocationLocationArbitratorTest : public testing::Test { scoped_refptr<FakeAccessTokenStore> access_token_store_; scoped_ptr<MockLocationObserver> observer_; - scoped_ptr<TestingGeolocationArbitrator> arbitrator_; + scoped_ptr<TestingLocationArbitrator> arbitrator_; base::MessageLoop loop_; }; diff --git a/chromium/content/browser/geolocation/mock_location_arbitrator.cc b/chromium/content/browser/geolocation/mock_location_arbitrator.cc index e0508e11ecf..f48915e07f1 100644 --- a/chromium/content/browser/geolocation/mock_location_arbitrator.cc +++ b/chromium/content/browser/geolocation/mock_location_arbitrator.cc @@ -9,24 +9,24 @@ namespace content { -MockGeolocationArbitrator::MockGeolocationArbitrator() +MockLocationArbitrator::MockLocationArbitrator() : permission_granted_(false), providers_started_(false) { } -void MockGeolocationArbitrator::StartProviders(bool use_high_accuracy) { +void MockLocationArbitrator::StartProviders(bool use_high_accuracy) { providers_started_ = true;; } -void MockGeolocationArbitrator::StopProviders() { +void MockLocationArbitrator::StopProviders() { providers_started_ = false; } -void MockGeolocationArbitrator::OnPermissionGranted() { +void MockLocationArbitrator::OnPermissionGranted() { permission_granted_ = true; } -bool MockGeolocationArbitrator::HasPermissionBeenGranted() const { +bool MockLocationArbitrator::HasPermissionBeenGranted() const { return permission_granted_; } diff --git a/chromium/content/browser/geolocation/mock_location_arbitrator.h b/chromium/content/browser/geolocation/mock_location_arbitrator.h index 20ef672842a..f096b12f720 100644 --- a/chromium/content/browser/geolocation/mock_location_arbitrator.h +++ b/chromium/content/browser/geolocation/mock_location_arbitrator.h @@ -13,13 +13,13 @@ namespace content { struct Geoposition; -class MockGeolocationArbitrator : public GeolocationArbitrator { +class MockLocationArbitrator : public LocationArbitrator { public: - MockGeolocationArbitrator(); + MockLocationArbitrator(); bool providers_started() const { return providers_started_; } - // GeolocationArbitrator: + // LocationArbitrator: virtual void StartProviders(bool use_high_accuracy) OVERRIDE; virtual void StopProviders() OVERRIDE; @@ -30,7 +30,7 @@ class MockGeolocationArbitrator : public GeolocationArbitrator { bool permission_granted_; bool providers_started_; - DISALLOW_COPY_AND_ASSIGN(MockGeolocationArbitrator); + DISALLOW_COPY_AND_ASSIGN(MockLocationArbitrator); }; } // namespace content diff --git a/chromium/content/browser/geolocation/mock_location_provider.h b/chromium/content/browser/geolocation/mock_location_provider.h index 342effcac5d..dd7471fb237 100644 --- a/chromium/content/browser/geolocation/mock_location_provider.h +++ b/chromium/content/browser/geolocation/mock_location_provider.h @@ -45,7 +45,7 @@ class MockLocationProvider : public LocationProviderBase { }; // Factory functions for the various sorts of mock location providers, -// for use with GeolocationArbitrator::SetProviderFactoryForTest (i.e. +// for use with LocationArbitrator::SetProviderFactoryForTest (i.e. // not intended for test code to use to get access to the mock, you can use // MockLocationProvider::instance_ for this, or make a custom factory method). diff --git a/chromium/content/browser/geolocation/network_location_provider.cc b/chromium/content/browser/geolocation/network_location_provider.cc index 82171a062b9..678edb24092 100644 --- a/chromium/content/browser/geolocation/network_location_provider.cc +++ b/chromium/content/browser/geolocation/network_location_provider.cc @@ -11,7 +11,7 @@ namespace content { namespace { -// The maximum period of time we'll wait for a complete set of device data +// The maximum period of time we'll wait for a complete set of wifi data // before sending the request. const int kDataCompleteWaitSeconds = 2; } // namespace @@ -26,7 +26,7 @@ NetworkLocationProvider::PositionCache::~PositionCache() {} bool NetworkLocationProvider::PositionCache::CachePosition( const WifiData& wifi_data, const Geoposition& position) { - // Check that we can generate a valid key for the device data. + // Check that we can generate a valid key for the wifi data. string16 key; if (!MakeKey(wifi_data, &key)) { return false; @@ -53,8 +53,8 @@ bool NetworkLocationProvider::PositionCache::CachePosition( return true; } -// Searches for a cached position response for the current set of cell ID and -// WiFi data. Returns the cached position if available, NULL otherwise. +// Searches for a cached position response for the current WiFi data. Returns +// the cached position if available, NULL otherwise. const Geoposition* NetworkLocationProvider::PositionCache::FindPosition( const WifiData& wifi_data) { string16 key; @@ -65,15 +65,14 @@ const Geoposition* NetworkLocationProvider::PositionCache::FindPosition( return iter == cache_.end() ? NULL : &iter->second; } -// Makes the key for the map of cached positions, using a set of -// device data. Returns true if a good key was generated, false otherwise. +// Makes the key for the map of cached positions, using the available data. +// Returns true if a good key was generated, false otherwise. // // static bool NetworkLocationProvider::PositionCache::MakeKey( const WifiData& wifi_data, string16* key) { - // Currently we use only the WiFi data, and base the key only on - // the MAC addresses. + // Currently we use only WiFi data and base the key only on the MAC addresses. DCHECK(key); key->clear(); const size_t kCharsPerMacAddress = 6 * 3 + 1; // e.g. "11:22:33:44:55:66|" @@ -88,7 +87,7 @@ bool NetworkLocationProvider::PositionCache::MakeKey( *key += separator; } // If the key is the empty string, return false, as we don't want to cache a - // position for such a set of device data. + // position for such data. return !key->empty(); } @@ -110,6 +109,9 @@ NetworkLocationProvider::NetworkLocationProvider( const string16& access_token) : access_token_store_(access_token_store), wifi_data_provider_(NULL), + wifi_data_update_callback_( + base::Bind(&NetworkLocationProvider::WifiDataUpdateAvailable, + base::Unretained(this))), is_wifi_data_complete_(false), access_token_(access_token), is_permission_granted_(false), @@ -118,7 +120,10 @@ NetworkLocationProvider::NetworkLocationProvider( // Create the position cache. position_cache_.reset(new PositionCache()); - request_.reset(new NetworkLocationRequest(url_context_getter, url, this)); + NetworkLocationRequest::LocationResponseCallback callback = + base::Bind(&NetworkLocationProvider::LocationResponseAvailable, + base::Unretained(this)); + request_.reset(new NetworkLocationRequest(url_context_getter, url, callback)); } NetworkLocationProvider::~NetworkLocationProvider() { @@ -126,7 +131,7 @@ NetworkLocationProvider::~NetworkLocationProvider() { } // LocationProvider implementation -void NetworkLocationProvider::GetPosition(Geoposition *position) { +void NetworkLocationProvider::GetPosition(Geoposition* position) { DCHECK(position); *position = position_; } @@ -148,15 +153,13 @@ void NetworkLocationProvider::OnPermissionGranted() { } } -// DeviceDataProviderInterface::ListenerInterface implementation. -void NetworkLocationProvider::DeviceDataUpdateAvailable( +void NetworkLocationProvider::WifiDataUpdateAvailable( WifiDataProvider* provider) { DCHECK(provider == wifi_data_provider_); is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); - OnDeviceDataUpdated(); + OnWifiDataUpdated(); } -// NetworkLocationRequest::ListenerInterface implementation. void NetworkLocationProvider::LocationResponseAvailable( const Geoposition& position, bool server_error, @@ -190,26 +193,35 @@ bool NetworkLocationProvider::StartProvider(bool high_accuracy) { return false; } - // Get the device data providers. The first call to Register will create the - // provider and it will be deleted by ref counting. - wifi_data_provider_ = WifiDataProvider::Register(this); + // Registers a callback with the data provider. The first call to Register + // will create a singleton data provider and it will be deleted when the last + // callback is removed with Unregister. + wifi_data_provider_ = WifiDataProvider::Register(&wifi_data_update_callback_); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&NetworkLocationProvider::RequestPosition, weak_factory_.GetWeakPtr()), base::TimeDelta::FromSeconds(kDataCompleteWaitSeconds)); - // Get the device data. + // Get the wifi data. is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); if (is_wifi_data_complete_) - OnDeviceDataUpdated(); + OnWifiDataUpdated(); return true; } +void NetworkLocationProvider::OnWifiDataUpdated() { + DCHECK(CalledOnValidThread()); + wifi_data_updated_timestamp_ = base::Time::Now(); + + is_new_data_available_ = is_wifi_data_complete_; + RequestRefresh(); +} + void NetworkLocationProvider::StopProvider() { DCHECK(CalledOnValidThread()); if (IsStarted()) { - wifi_data_provider_->Unregister(this); + wifi_data_provider_->Unregister(&wifi_data_update_callback_); } wifi_data_provider_ = NULL; weak_factory_.InvalidateWeakPtrs(); @@ -223,7 +235,7 @@ void NetworkLocationProvider::RequestPosition() { const Geoposition* cached_position = position_cache_->FindPosition(wifi_data_); - DCHECK(!device_data_updated_timestamp_.is_null()) << + DCHECK(!wifi_data_updated_timestamp_.is_null()) << "Timestamp must be set before looking up position"; if (cached_position) { DCHECK(cached_position->Validate()); @@ -232,7 +244,7 @@ void NetworkLocationProvider::RequestPosition() { // The timestamp of a position fix is determined by the timestamp // of the source data update. (The value of position_.timestamp from // the cache could be from weeks ago!) - position_.timestamp = device_data_updated_timestamp_; + position_.timestamp = wifi_data_updated_timestamp_; is_new_data_available_ = false; // Let listeners know that we now have a position available. NotifyCallback(position_); @@ -253,15 +265,7 @@ void NetworkLocationProvider::RequestPosition() { << wifi_data_.access_point_data.size(); } request_->MakeRequest(access_token_, wifi_data_, - device_data_updated_timestamp_); -} - -void NetworkLocationProvider::OnDeviceDataUpdated() { - DCHECK(CalledOnValidThread()); - device_data_updated_timestamp_ = base::Time::Now(); - - is_new_data_available_ = is_wifi_data_complete_; - RequestRefresh(); + wifi_data_updated_timestamp_); } bool NetworkLocationProvider::IsStarted() const { diff --git a/chromium/content/browser/geolocation/network_location_provider.h b/chromium/content/browser/geolocation/network_location_provider.h index a1ba5b38673..d5f3e999d7e 100644 --- a/chromium/content/browser/geolocation/network_location_provider.h +++ b/chromium/content/browser/geolocation/network_location_provider.h @@ -15,9 +15,9 @@ #include "base/strings/string16.h" #include "base/threading/non_thread_safe.h" #include "base/threading/thread.h" -#include "content/browser/geolocation/device_data_provider.h" #include "content/browser/geolocation/location_provider_base.h" #include "content/browser/geolocation/network_location_request.h" +#include "content/browser/geolocation/wifi_data_provider.h" #include "content/common/content_export.h" #include "content/public/common/geoposition.h" @@ -27,15 +27,12 @@ class AccessTokenStore; class NetworkLocationProvider : public base::NonThreadSafe, - public LocationProviderBase, - public WifiDataProvider::ListenerInterface, - public NetworkLocationRequest::ListenerInterface { + public LocationProviderBase { public: // Cache of recently resolved locations. Public for tests. class CONTENT_EXPORT PositionCache { public: - // The maximum size of the cache of positions for previously requested - // device data. + // The maximum size of the cache of positions. static const size_t kMaximumSize; PositionCache(); @@ -48,19 +45,19 @@ class NetworkLocationProvider bool CachePosition(const WifiData& wifi_data, const Geoposition& position); - // Searches for a cached position response for the current set of device - // data. Returns NULL if the position is not in the cache, or the cached + // Searches for a cached position response for the current set of data. + // Returns NULL if the position is not in the cache, or the cached // position if available. Ownership remains with the cache. const Geoposition* FindPosition(const WifiData& wifi_data); private: // Makes the key for the map of cached positions, using a set of - // device data. Returns true if a good key was generated, false otherwise. + // data. Returns true if a good key was generated, false otherwise. static bool MakeKey(const WifiData& wifi_data, string16* key); // The cache of positions. This is stored as a map keyed on a string that - // represents a set of device data, and a list to provide + // represents a set of data, and a list to provide // least-recently-added eviction. typedef std::map<string16, Geoposition> CacheMap; CacheMap cache_; @@ -85,31 +82,32 @@ class NetworkLocationProvider // Satisfies a position request from cache or network. void RequestPosition(); - // Internal helper used by DeviceDataUpdateAvailable - void OnDeviceDataUpdated(); + // Called from a callback when new wifi data is available. + void WifiDataUpdateAvailable(WifiDataProvider* provider); - bool IsStarted() const; + // Internal helper used by WifiDataUpdateAvailable. + void OnWifiDataUpdated(); - // DeviceDataProvider::ListenerInterface implementation. - virtual void DeviceDataUpdateAvailable(WifiDataProvider* provider) OVERRIDE; + bool IsStarted() const; - // NetworkLocationRequest::ListenerInterface implementation. - virtual void LocationResponseAvailable(const Geoposition& position, - bool server_error, - const string16& access_token, - const WifiData& wifi_data) OVERRIDE; + void LocationResponseAvailable(const Geoposition& position, + bool server_error, + const string16& access_token, + const WifiData& wifi_data); scoped_refptr<AccessTokenStore> access_token_store_; // The wifi data provider, acquired via global factories. WifiDataProvider* wifi_data_provider_; - // The wifi data, flags to indicate if the data set is complete. + WifiDataProvider::WifiDataUpdateCallback wifi_data_update_callback_; + + // The wifi data and a flag to indicate if the data set is complete. WifiData wifi_data_; bool is_wifi_data_complete_; - // The timestamp for the latest device data update. - base::Time device_data_updated_timestamp_; + // The timestamp for the latest wifi data update. + base::Time wifi_data_updated_timestamp_; // Cached value loaded from the token store or set by a previous server // response, and sent in each subsequent network request. @@ -126,10 +124,11 @@ class NetworkLocationProvider // The network location request object, and the url it uses. scoped_ptr<NetworkLocationRequest> request_; - base::WeakPtrFactory<NetworkLocationProvider> weak_factory_; // The cache of positions. scoped_ptr<PositionCache> position_cache_; + base::WeakPtrFactory<NetworkLocationProvider> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(NetworkLocationProvider); }; diff --git a/chromium/content/browser/geolocation/network_location_provider_unittest.cc b/chromium/content/browser/geolocation/network_location_provider_unittest.cc index edfada7766a..f7c732049a8 100644 --- a/chromium/content/browser/geolocation/network_location_provider_unittest.cc +++ b/chromium/content/browser/geolocation/network_location_provider_unittest.cc @@ -46,55 +46,49 @@ class MessageLoopQuitListener { const LocationProvider* updated_provider_; }; -// A mock implementation of DeviceDataProviderImplBase for testing. Adapted from +// A mock implementation of WifiDataProviderImplBase for testing. Adapted from // http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc -template<typename DataType> -class MockDeviceDataProviderImpl - : public DeviceDataProviderImplBase<DataType> { +class MockWifiDataProviderImpl : public WifiDataProviderImplBase { public: - // Factory method for use with DeviceDataProvider::SetFactory. - static DeviceDataProviderImplBase<DataType>* GetInstance() { + // Factory method for use with WifiDataProvider::SetFactory. + static WifiDataProviderImplBase* GetInstance() { CHECK(instance_); return instance_; } - static MockDeviceDataProviderImpl<DataType>* CreateInstance() { + static MockWifiDataProviderImpl* CreateInstance() { CHECK(!instance_); - instance_ = new MockDeviceDataProviderImpl<DataType>; + instance_ = new MockWifiDataProviderImpl; return instance_; } - MockDeviceDataProviderImpl() + MockWifiDataProviderImpl() : start_calls_(0), stop_calls_(0), got_data_(true) { } - virtual ~MockDeviceDataProviderImpl() { - CHECK(this == instance_); - instance_ = NULL; - } - - // DeviceDataProviderImplBase implementation. - virtual bool StartDataProvider() { + // WifiDataProviderImplBase implementation. + virtual void StartDataProvider() OVERRIDE { ++start_calls_; - return true; } - virtual void StopDataProvider() { + + virtual void StopDataProvider() OVERRIDE { ++stop_calls_; } - virtual bool GetData(DataType* data_out) { + + virtual bool GetData(WifiData* data_out) OVERRIDE { CHECK(data_out); *data_out = data_; return got_data_; } - void SetData(const DataType& new_data) { + void SetData(const WifiData& new_data) { got_data_ = true; const bool differs = data_.DiffersSignificantly(new_data); data_ = new_data; if (differs) - this->NotifyListeners(); + this->RunCallbacks(); } void set_got_data(bool got_data) { got_data_ = got_data; } @@ -102,17 +96,20 @@ class MockDeviceDataProviderImpl int stop_calls_; private: - static MockDeviceDataProviderImpl<DataType>* instance_; + virtual ~MockWifiDataProviderImpl() { + CHECK(this == instance_); + instance_ = NULL; + } + + static MockWifiDataProviderImpl* instance_; - DataType data_; + WifiData data_; bool got_data_; - DISALLOW_COPY_AND_ASSIGN(MockDeviceDataProviderImpl); + DISALLOW_COPY_AND_ASSIGN(MockWifiDataProviderImpl); }; -template<typename DataType> -MockDeviceDataProviderImpl<DataType>* -MockDeviceDataProviderImpl<DataType>::instance_ = NULL; +MockWifiDataProviderImpl* MockWifiDataProviderImpl::instance_ = NULL; // Main test fixture class GeolocationNetworkProviderTest : public testing::Test { @@ -121,7 +118,7 @@ class GeolocationNetworkProviderTest : public testing::Test { test_server_url_ = GURL(kTestServerUrl); access_token_store_ = new FakeAccessTokenStore; wifi_data_provider_ = - MockDeviceDataProviderImpl<WifiData>::CreateInstance(); + MockWifiDataProviderImpl::CreateInstance(); } virtual void TearDown() { @@ -143,8 +140,7 @@ class GeolocationNetworkProviderTest : public testing::Test { GeolocationNetworkProviderTest() { // TODO(joth): Really these should be in SetUp, not here, but they take no // effect on Mac OS Release builds if done there. I kid not. Figure out why. - WifiDataProvider::SetFactory( - MockDeviceDataProviderImpl<WifiData>::GetInstance); + WifiDataProvider::SetFactory(MockWifiDataProviderImpl::GetInstance); } // Returns the current url fetcher (if any) and advances the id ready for the @@ -263,7 +259,7 @@ class GeolocationNetworkProviderTest : public testing::Test { // Check to see that the api key is being appended for the default // network provider url. bool is_default_url = UrlWithoutQuery(request_url) == - UrlWithoutQuery(GeolocationArbitratorImpl::DefaultNetworkProviderURL()); + UrlWithoutQuery(LocationArbitratorImpl::DefaultNetworkProviderURL()); EXPECT_EQ(is_default_url, !request_url.query().empty()); const std::string& upload_data = request.upload_data(); @@ -325,7 +321,7 @@ class GeolocationNetworkProviderTest : public testing::Test { base::MessageLoop main_message_loop_; scoped_refptr<FakeAccessTokenStore> access_token_store_; net::TestURLFetcherFactory url_fetcher_factory_; - scoped_refptr<MockDeviceDataProviderImpl<WifiData> > wifi_data_provider_; + scoped_refptr<MockWifiDataProviderImpl> wifi_data_provider_; }; TEST_F(GeolocationNetworkProviderTest, CreateDestroy) { @@ -346,7 +342,7 @@ TEST_F(GeolocationNetworkProviderTest, StartProvider) { } TEST_F(GeolocationNetworkProviderTest, StartProviderDefaultUrl) { - test_server_url_ = GeolocationArbitratorImpl::DefaultNetworkProviderURL(); + test_server_url_ = LocationArbitratorImpl::DefaultNetworkProviderURL(); scoped_ptr<LocationProvider> provider(CreateProvider(true)); EXPECT_TRUE(provider->StartProvider(false)); net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id(); @@ -492,7 +488,7 @@ TEST_F(GeolocationNetworkProviderTest, NoRequestOnStartupUntilWifiData) { } TEST_F(GeolocationNetworkProviderTest, NewDataReplacesExistingNetworkRequest) { - // Send initial request with empty device data + // Send initial request with empty data scoped_ptr<LocationProvider> provider(CreateProvider(true)); EXPECT_TRUE(provider->StartProvider(false)); net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id(); diff --git a/chromium/content/browser/geolocation/network_location_request.cc b/chromium/content/browser/geolocation/network_location_request.cc index 74a77e1f459..f15141a29e3 100644 --- a/chromium/content/browser/geolocation/network_location_request.cc +++ b/chromium/content/browser/geolocation/network_location_request.cc @@ -70,10 +70,10 @@ int NetworkLocationRequest::url_fetcher_id_for_tests = 0; NetworkLocationRequest::NetworkLocationRequest( net::URLRequestContextGetter* context, const GURL& url, - ListenerInterface* listener) - : url_context_(context), listener_(listener), + LocationResponseCallback callback) + : url_context_(context), + callback_(callback), url_(url) { - DCHECK(listener); } NetworkLocationRequest::~NetworkLocationRequest() { @@ -139,10 +139,8 @@ void NetworkLocationRequest::OnURLFetchComplete( 100); } - DCHECK(listener_); - DVLOG(1) << "NetworkLocationRequest::Run() : Calling listener with position."; - listener_->LocationResponseAvailable(position, server_error, access_token, - wifi_data_); + DVLOG(1) << "NetworkLocationRequest::OnURLFetchComplete() : run callback."; + callback_.Run(position, server_error, access_token, wifi_data_); } // Local functions. @@ -156,7 +154,7 @@ struct AccessPointLess { }; GURL FormRequestURL(const GURL& url) { - if (url == GeolocationArbitratorImpl::DefaultNetworkProviderURL()) { + if (url == LocationArbitratorImpl::DefaultNetworkProviderURL()) { std::string api_key = google_apis::GetAPIKey(); if (!api_key.empty()) { std::string query(url.query()); @@ -273,7 +271,7 @@ void GetLocationFromResponse(bool http_post_result, FormatPositionError(server_url, message, position); return; } - // We use the timestamp from the device data that was used to generate + // We use the timestamp from the wifi data that was used to generate // this position fix. if (!ParseServerResponse(response_body, timestamp, position, access_token)) { // We failed to parse the repsonse. diff --git a/chromium/content/browser/geolocation/network_location_request.h b/chromium/content/browser/geolocation/network_location_request.h index 36e843b4351..38aaf3e6ce7 100644 --- a/chromium/content/browser/geolocation/network_location_request.h +++ b/chromium/content/browser/geolocation/network_location_request.h @@ -8,7 +8,7 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "content/browser/geolocation/device_data_provider.h" +#include "content/browser/geolocation/wifi_data_provider.h" #include "content/common/content_export.h" #include "net/url_request/url_fetcher_delegate.h" #include "url/gurl.h" @@ -21,31 +21,26 @@ class URLRequestContextGetter; namespace content { struct Geoposition; -// Takes a set of device data and sends it to a server to get a position fix. +// Takes wifi data and sends it to a server to get a position fix. // It performs formatting of the request and interpretation of the response. class NetworkLocationRequest : private net::URLFetcherDelegate { public: // ID passed to URLFetcher::Create(). Used for testing. CONTENT_EXPORT static int url_fetcher_id_for_tests; - // Interface for receiving callbacks from a NetworkLocationRequest object. - class ListenerInterface { - public: - // Updates the listener with a new position. server_error indicates whether - // was a server or network error - either no response or a 500 error code. - virtual void LocationResponseAvailable( - const Geoposition& position, - bool server_error, - const string16& access_token, - const WifiData& wifi_data) = 0; - - protected: - virtual ~ListenerInterface() {} - }; + + // Called when a new geo position is available. The second argument indicates + // whether there was a server error or not. It is true when there was a + // server or network error - either no response or a 500 error code. + typedef base::Callback<void(const Geoposition& /* position */, + bool /* server_error */, + const string16& /* access_token */, + const WifiData& /* wifi_data */)> + LocationResponseCallback; // |url| is the server address to which the request wil be sent. NetworkLocationRequest(net::URLRequestContextGetter* context, const GURL& url, - ListenerInterface* listener); + LocationResponseCallback callback); virtual ~NetworkLocationRequest(); // Makes a new request. Returns true if the new request was successfully @@ -62,7 +57,7 @@ class NetworkLocationRequest : private net::URLFetcherDelegate { virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; scoped_refptr<net::URLRequestContextGetter> url_context_; - ListenerInterface* listener_; + LocationResponseCallback callback_; const GURL url_; scoped_ptr<net::URLFetcher> url_fetcher_; diff --git a/chromium/content/browser/geolocation/device_data_provider.cc b/chromium/content/browser/geolocation/wifi_data.cc index 00c585bd11d..f4ea267b2f1 100644 --- a/chromium/content/browser/geolocation/device_data_provider.cc +++ b/chromium/content/browser/geolocation/wifi_data.cc @@ -1,16 +1,12 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 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/browser/geolocation/device_data_provider.h" +#include "content/browser/geolocation/wifi_data.h" -namespace content { +#include "base/logging.h" -// statics -template<> DeviceDataProvider<WifiData>* - DeviceDataProvider<WifiData>::instance_ = NULL; -template<> DeviceDataProvider<WifiData>::ImplFactoryFunction - DeviceDataProvider<WifiData>::factory_function_ = DefaultFactoryFunction; +namespace content { AccessPointData::AccessPointData() : radio_signal_strength(kint32min), @@ -35,7 +31,7 @@ bool WifiData::DiffersSignificantly(const WifiData& other) const { min_ap_count / 2); if (max_ap_count > min_ap_count + difference_threadhold) return true; - // Compute size of interesction of old and new sets. + // Compute size of intersection of old and new sets. size_t num_common = 0; for (AccessPointDataSet::const_iterator iter = access_point_data.begin(); iter != access_point_data.end(); diff --git a/chromium/content/browser/geolocation/wifi_data.h b/chromium/content/browser/geolocation/wifi_data.h new file mode 100644 index 00000000000..a24e42e2ac1 --- /dev/null +++ b/chromium/content/browser/geolocation/wifi_data.h @@ -0,0 +1,54 @@ +// Copyright 2013 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_BROWSER_GEOLOCATION_WIFI_DATA_H_ +#define CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_H_ + +#include <set> + +#include "base/basictypes.h" +#include "base/strings/string16.h" +#include "content/common/content_export.h" + +namespace content { + +// Wifi data relating to a single access point. +struct CONTENT_EXPORT AccessPointData { + AccessPointData(); + ~AccessPointData(); + + // MAC address, formatted as per MacAddressAsString16. + string16 mac_address; + int radio_signal_strength; // Measured in dBm + int channel; + int signal_to_noise; // Ratio in dB + string16 ssid; // Network identifier +}; + +// This is to allow AccessPointData to be used in std::set. We order +// lexicographically by MAC address. +struct AccessPointDataLess { + bool operator()(const AccessPointData& data1, + const AccessPointData& data2) const { + return data1.mac_address < data2.mac_address; + } +}; + +// All data for wifi. +struct CONTENT_EXPORT WifiData { + WifiData(); + ~WifiData(); + + // Determines whether a new set of WiFi data differs significantly from this. + bool DiffersSignificantly(const WifiData& other) const; + + // Store access points as a set, sorted by MAC address. This allows quick + // comparison of sets for detecting changes and for caching. + typedef std::set<AccessPointData, AccessPointDataLess> AccessPointDataSet; + AccessPointDataSet access_point_data; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_H_ diff --git a/chromium/content/browser/geolocation/wifi_data_provider.cc b/chromium/content/browser/geolocation/wifi_data_provider.cc new file mode 100644 index 00000000000..85595f927ab --- /dev/null +++ b/chromium/content/browser/geolocation/wifi_data_provider.cc @@ -0,0 +1,147 @@ +// Copyright 2013 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/browser/geolocation/wifi_data_provider.h" + +namespace content { + +// static +WifiDataProvider* WifiDataProvider::instance_ = NULL; + +// static +WifiDataProvider::ImplFactoryFunction WifiDataProvider::factory_function_ = + DefaultFactoryFunction; + +// static +void WifiDataProvider::SetFactory(ImplFactoryFunction factory_function_in) { + factory_function_ = factory_function_in; +} + +// static +void WifiDataProvider::ResetFactory() { + factory_function_ = DefaultFactoryFunction; +} + +// static +WifiDataProvider* WifiDataProvider::Register(WifiDataUpdateCallback* callback) { + bool need_to_start_data_provider = false; + if (!instance_) { + instance_ = new WifiDataProvider(); + need_to_start_data_provider = true; + } + DCHECK(instance_); + instance_->AddCallback(callback); + // Start the provider after adding the callback, to avoid any race in + // it running early. + if (need_to_start_data_provider) + instance_->StartDataProvider(); + return instance_; +} + +// static +bool WifiDataProvider::Unregister(WifiDataUpdateCallback* callback) { + DCHECK(instance_); + DCHECK(instance_->has_callbacks()); + if (!instance_->RemoveCallback(callback)) { + return false; + } + if (!instance_->has_callbacks()) { + // Must stop the data provider (and any implementation threads) before + // destroying to avoid any race conditions in access to the provider in + // the destructor chain. + instance_->StopDataProvider(); + delete instance_; + instance_ = NULL; + } + return true; +} + +WifiDataProviderImplBase::WifiDataProviderImplBase() + : container_(NULL), + client_loop_(base::MessageLoop::current()) { + DCHECK(client_loop_); +} + +WifiDataProviderImplBase::~WifiDataProviderImplBase() { +} + +void WifiDataProviderImplBase::SetContainer(WifiDataProvider* container) { + container_ = container; +} + +void WifiDataProviderImplBase::AddCallback(WifiDataUpdateCallback* callback) { + callbacks_.insert(callback); +} + +bool WifiDataProviderImplBase::RemoveCallback( + WifiDataUpdateCallback* callback) { + return callbacks_.erase(callback) == 1; +} + +bool WifiDataProviderImplBase::has_callbacks() const { + return !callbacks_.empty(); +} + +void WifiDataProviderImplBase::RunCallbacks() { + client_loop_->PostTask(FROM_HERE, base::Bind( + &WifiDataProviderImplBase::DoRunCallbacks, + this)); +} + +bool WifiDataProviderImplBase::CalledOnClientThread() const { + return base::MessageLoop::current() == this->client_loop_; +} + +base::MessageLoop* WifiDataProviderImplBase::client_loop() const { + return client_loop_; +} + +void WifiDataProviderImplBase::DoRunCallbacks() { + // It's possible that all the callbacks (and the container) went away + // whilst this task was pending. This is fine; the loop will be a no-op. + CallbackSet::const_iterator iter = callbacks_.begin(); + while (iter != callbacks_.end()) { + WifiDataUpdateCallback* callback = *iter; + ++iter; // Advance iter before running, in case callback unregisters. + callback->Run(container_); + } +} + +WifiDataProvider::WifiDataProvider() { + DCHECK(factory_function_); + impl_ = (*factory_function_)(); + DCHECK(impl_.get()); + impl_->SetContainer(this); +} + +WifiDataProvider::~WifiDataProvider() { + DCHECK(impl_.get()); + impl_->SetContainer(NULL); +} + +bool WifiDataProvider::GetData(WifiData* data) { + return impl_->GetData(data); +} + +void WifiDataProvider::AddCallback(WifiDataUpdateCallback* callback) { + impl_->AddCallback(callback); +} + +bool WifiDataProvider::RemoveCallback(WifiDataUpdateCallback* callback) { + return impl_->RemoveCallback(callback); +} + +bool WifiDataProvider::has_callbacks() const { + return impl_->has_callbacks(); +} + +void WifiDataProvider::StartDataProvider() { + impl_->StartDataProvider(); +} + +void WifiDataProvider::StopDataProvider() { + impl_->StopDataProvider(); +} + +} // namespace content diff --git a/chromium/content/browser/geolocation/wifi_data_provider.h b/chromium/content/browser/geolocation/wifi_data_provider.h new file mode 100644 index 00000000000..bfb06ec5444 --- /dev/null +++ b/chromium/content/browser/geolocation/wifi_data_provider.h @@ -0,0 +1,162 @@ +// Copyright 2013 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. + +// A wifi data provider provides wifi data from the device that is used by a +// NetworkLocationProvider to obtain a position fix. We use a singleton +// instance of the wifi data provider, which is used by multiple +// NetworkLocationProvider objects. +// +// This file provides WifiDataProvider, which provides static methods to +// access the singleton instance. The singleton instance uses a private +// implementation to abstract across platforms and also to allow mock providers +// to be used for testing. +// +// This file also provides WifiDataProviderImplBase, a base class which +// provides common functionality for the private implementations. + +#ifndef CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_H_ +#define CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_H_ + +#include <set> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "content/browser/geolocation/wifi_data.h" +#include "content/common/content_export.h" + +namespace content { + +class WifiDataProvider; + +// See class WifiDataProvider for the public client API. +// WifiDataProvider uses containment to hide platform-specific implementation +// details from common code. This class provides common functionality for these +// contained implementation classes. This is a modified pimpl pattern. +class CONTENT_EXPORT WifiDataProviderImplBase + : public base::RefCountedThreadSafe<WifiDataProviderImplBase> { + public: + WifiDataProviderImplBase(); + + // Tells the provider to start looking for data. Callbacks will start + // receiving notifications after this call. + virtual void StartDataProvider() = 0; + + // Tells the provider to stop looking for data. Callbacks will stop + // receiving notifications after this call. + virtual void StopDataProvider() = 0; + + // Provides whatever data the provider has, which may be nothing. Return + // value indicates whether this is all the data the provider could ever + // obtain. + virtual bool GetData(WifiData* data) = 0; + + // Sets the container of this class, which is of type WifiDataProvider. + // This is required to pass as a parameter when calling a callback. + void SetContainer(WifiDataProvider* container); + + typedef base::Callback<void(WifiDataProvider*)> WifiDataUpdateCallback; + + void AddCallback(WifiDataUpdateCallback* callback); + + bool RemoveCallback(WifiDataUpdateCallback* callback); + + bool has_callbacks() const; + + protected: + friend class base::RefCountedThreadSafe<WifiDataProviderImplBase>; + virtual ~WifiDataProviderImplBase(); + + typedef std::set<WifiDataUpdateCallback*> CallbackSet; + + // Runs all callbacks via a posted task, so we can unwind callstack here and + // avoid client reentrancy. + void RunCallbacks(); + + bool CalledOnClientThread() const; + + base::MessageLoop* client_loop() const; + + private: + void DoRunCallbacks(); + + WifiDataProvider* container_; + + // Reference to the client's message loop. All callbacks should happen in this + // context. + base::MessageLoop* client_loop_; + + CallbackSet callbacks_; + + DISALLOW_COPY_AND_ASSIGN(WifiDataProviderImplBase); +}; + +// A wifi data provider +// +// We use a singleton instance of this class which is shared by multiple network +// location providers. These location providers access the instance through the +// Register and Unregister methods. +class CONTENT_EXPORT WifiDataProvider { + public: + // Sets the factory function which will be used by Register to create the + // implementation used by the singleton instance. This factory approach is + // used both to abstract accross platform-specific implementations and to + // inject mock implementations for testing. + typedef WifiDataProviderImplBase* (*ImplFactoryFunction)(void); + static void SetFactory(ImplFactoryFunction factory_function_in); + + // Resets the factory function to the default. + static void ResetFactory(); + + typedef base::Callback<void(WifiDataProvider*)> WifiDataUpdateCallback; + + // Registers a callback, which will be run whenever new data is available. + // Instantiates the singleton if necessary, and always returns it. + static WifiDataProvider* Register(WifiDataUpdateCallback* callback); + + // Removes a callback. If this is the last callback, deletes the singleton + // instance. Return value indicates success. + static bool Unregister(WifiDataUpdateCallback* callback); + + // Provides whatever data the provider has, which may be nothing. Return + // value indicates whether this is all the data the provider could ever + // obtain. + bool GetData(WifiData* data); + + private: + // Private constructor and destructor, callers access singleton through + // Register and Unregister. + WifiDataProvider(); + virtual ~WifiDataProvider(); + + void AddCallback(WifiDataUpdateCallback* callback); + bool RemoveCallback(WifiDataUpdateCallback* callback); + bool has_callbacks() const; + + void StartDataProvider(); + void StopDataProvider(); + + static WifiDataProviderImplBase* DefaultFactoryFunction(); + + // The singleton-like instance of this class. (Not 'true' singleton, as it + // may go through multiple create/destroy/create cycles per process instance, + // e.g. when under test). + static WifiDataProvider* instance_; + + // The factory function used to create the singleton instance. + static ImplFactoryFunction factory_function_; + + // The internal implementation. + scoped_refptr<WifiDataProviderImplBase> impl_; + + DISALLOW_COPY_AND_ASSIGN(WifiDataProvider); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_H_ diff --git a/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc b/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc index b4222d7a01b..a8b20414ceb 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_chromeos.cc @@ -29,18 +29,17 @@ WifiDataProviderChromeOs::WifiDataProviderChromeOs() : started_(false) { WifiDataProviderChromeOs::~WifiDataProviderChromeOs() { } -bool WifiDataProviderChromeOs::StartDataProvider() { +void WifiDataProviderChromeOs::StartDataProvider() { DCHECK(CalledOnClientThread()); DCHECK(polling_policy_ == NULL); polling_policy_.reset( - new GenericPollingPolicy<kDefaultPollingIntervalMilliseconds, - kNoChangePollingIntervalMilliseconds, - kTwoNoChangePollingIntervalMilliseconds, - kNoWifiPollingIntervalMilliseconds>); + new GenericWifiPollingPolicy<kDefaultPollingIntervalMilliseconds, + kNoChangePollingIntervalMilliseconds, + kTwoNoChangePollingIntervalMilliseconds, + kNoWifiPollingIntervalMilliseconds>); ScheduleStart(); - return true; } void WifiDataProviderChromeOs::StopDataProvider() { @@ -71,14 +70,14 @@ void WifiDataProviderChromeOs::DoWifiScanTaskOnUIThread() { WifiData new_data; - if (!GetAccessPointData(&new_data.access_point_data)) { + if (GetAccessPointData(&new_data.access_point_data)) { client_loop()->PostTask( FROM_HERE, - base::Bind(&WifiDataProviderChromeOs::DidWifiScanTaskNoResults, this)); + base::Bind(&WifiDataProviderChromeOs::DidWifiScanTask, this, new_data)); } else { client_loop()->PostTask( FROM_HERE, - base::Bind(&WifiDataProviderChromeOs::DidWifiScanTask, this, new_data)); + base::Bind(&WifiDataProviderChromeOs::DidWifiScanTaskNoResults, this)); } } @@ -88,7 +87,6 @@ void WifiDataProviderChromeOs::DidWifiScanTaskNoResults() { // in between DoWifiScanTaskOnUIThread and this method). if (started_) ScheduleNextScan(polling_policy_->NoWifiInterval()); - MaybeNotifyListeners(false); } void WifiDataProviderChromeOs::DidWifiScanTask(const WifiData& new_data) { @@ -101,13 +99,10 @@ void WifiDataProviderChromeOs::DidWifiScanTask(const WifiData& new_data) { polling_policy_->UpdatePollingInterval(update_available); ScheduleNextScan(polling_policy_->PollingInterval()); } - MaybeNotifyListeners(update_available); -} -void WifiDataProviderChromeOs::MaybeNotifyListeners(bool update_available) { if (update_available || !is_first_scan_complete_) { is_first_scan_complete_ = true; - NotifyListeners(); + RunCallbacks(); } } @@ -141,9 +136,12 @@ void WifiDataProviderChromeOs::ScheduleStart() { bool WifiDataProviderChromeOs::GetAccessPointData( WifiData::AccessPointDataSet* result) { - chromeos::WifiAccessPointVector access_points; + // If wifi isn't enabled, we've effectively completed the task. + // Return true to indicate an empty access point list. if (!chromeos::NetworkHandler::Get()->geolocation_handler()->wifi_enabled()) - return false; + return true; + + chromeos::WifiAccessPointVector access_points; int64 age_ms = 0; if (!chromeos::NetworkHandler::Get()->geolocation_handler()-> GetWifiAccessPoints(&access_points, &age_ms)) { @@ -168,7 +166,6 @@ bool WifiDataProviderChromeOs::GetAccessPointData( } // static -template<> WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { return new WifiDataProviderChromeOs(); } diff --git a/chromium/content/browser/geolocation/wifi_data_provider_chromeos.h b/chromium/content/browser/geolocation/wifi_data_provider_chromeos.h index ac8902c9874..895d06eaf31 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_chromeos.h +++ b/chromium/content/browser/geolocation/wifi_data_provider_chromeos.h @@ -6,7 +6,8 @@ #define CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_CHROMEOS_H_ #include "base/compiler_specific.h" -#include "content/browser/geolocation/wifi_data_provider_common.h" +#include "content/browser/geolocation/wifi_data_provider.h" +#include "content/browser/geolocation/wifi_polling_policy.h" namespace content { @@ -16,7 +17,7 @@ class CONTENT_EXPORT WifiDataProviderChromeOs WifiDataProviderChromeOs(); // WifiDataProviderImplBase - virtual bool StartDataProvider() OVERRIDE; + virtual void StartDataProvider() OVERRIDE; virtual void StopDataProvider() OVERRIDE; virtual bool GetData(WifiData* data) OVERRIDE; @@ -31,7 +32,6 @@ class CONTENT_EXPORT WifiDataProviderChromeOs // Client thread void DidWifiScanTaskNoResults(); void DidWifiScanTask(const WifiData& new_data); - void MaybeNotifyListeners(bool update_available); // Will schedule a scan; i.e. enqueue DoWifiScanTask deferred task. void ScheduleNextScan(int interval); @@ -45,11 +45,8 @@ class CONTENT_EXPORT WifiDataProviderChromeOs // Get access point data from chromeos. bool GetAccessPointData(WifiData::AccessPointDataSet* data); - // Underlying OS wifi API. (UI thread) - scoped_ptr<WifiDataProviderCommon::WlanApiInterface> wlan_api_; - // Controls the polling update interval. (client thread) - scoped_ptr<PollingPolicyInterface> polling_policy_; + scoped_ptr<WifiPollingPolicy> polling_policy_; // The latest wifi data. (client thread) WifiData wifi_data_; diff --git a/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc b/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc index 541b28422b2..f9deb663539 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_chromeos_unittest.cc @@ -54,7 +54,7 @@ class GeolocationChromeOsWifiDataProviderTest : public testing::Test { shill::kGeoChannelProperty, channel); properties.SetStringWithoutPathExpansion( shill::kGeoSignalStrengthProperty, strength); - manager_test_->AddGeoNetwork(flimflam::kTypeWifi, properties); + manager_test_->AddGeoNetwork(shill::kTypeWifi, properties); } } message_loop_.RunUntilIdle(); diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common.cc b/chromium/content/browser/geolocation/wifi_data_provider_common.cc index 2642176358c..31f969c3a3c 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_common.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_common.cc @@ -25,40 +25,14 @@ string16 MacAddressAsString16(const uint8 mac_as_int[6]) { } WifiDataProviderCommon::WifiDataProviderCommon() - : Thread("Geolocation_wifi_provider"), - is_first_scan_complete_(false), + : is_first_scan_complete_(false), weak_factory_(this) { } WifiDataProviderCommon::~WifiDataProviderCommon() { - // Thread must be stopped before entering destructor chain to avoid race - // conditions; see comment in DeviceDataProvider::Unregister. - DCHECK(!IsRunning()); // Must call StopDataProvider before destroying me. } -bool WifiDataProviderCommon::StartDataProvider() { - DCHECK(CalledOnClientThread()); - DCHECK(!IsRunning()); // StartDataProvider must only be called once. - return Start(); -} - -void WifiDataProviderCommon::StopDataProvider() { - DCHECK(CalledOnClientThread()); - Stop(); -} - -bool WifiDataProviderCommon::GetData(WifiData* data) { - DCHECK(CalledOnClientThread()); - DCHECK(data); - base::AutoLock lock(data_mutex_); - *data = wifi_data_; - // If we've successfully completed a scan, indicate that we have all of the - // data we can get. - return is_first_scan_complete_; -} - -// Thread implementation -void WifiDataProviderCommon::Init() { +void WifiDataProviderCommon::StartDataProvider() { DCHECK(wlan_api_ == NULL); wlan_api_.reset(NewWlanApi()); if (wlan_api_ == NULL) { @@ -76,34 +50,37 @@ void WifiDataProviderCommon::Init() { ScheduleNextScan(0); } -void WifiDataProviderCommon::CleanUp() { - // Destroy these instances in the thread on which they were created. +void WifiDataProviderCommon::StopDataProvider() { wlan_api_.reset(); polling_policy_.reset(); } +bool WifiDataProviderCommon::GetData(WifiData* data) { + *data = wifi_data_; + // If we've successfully completed a scan, indicate that we have all of the + // data we can get. + return is_first_scan_complete_; +} + void WifiDataProviderCommon::DoWifiScanTask() { bool update_available = false; WifiData new_data; if (!wlan_api_->GetAccessPointData(&new_data.access_point_data)) { ScheduleNextScan(polling_policy_->NoWifiInterval()); } else { - { - base::AutoLock lock(data_mutex_); - update_available = wifi_data_.DiffersSignificantly(new_data); - wifi_data_ = new_data; - } + update_available = wifi_data_.DiffersSignificantly(new_data); + wifi_data_ = new_data; polling_policy_->UpdatePollingInterval(update_available); ScheduleNextScan(polling_policy_->PollingInterval()); } if (update_available || !is_first_scan_complete_) { is_first_scan_complete_ = true; - NotifyListeners(); + RunCallbacks(); } } void WifiDataProviderCommon::ScheduleNextScan(int interval) { - message_loop()->PostDelayedTask( + client_loop()->PostDelayedTask( FROM_HERE, base::Bind(&WifiDataProviderCommon::DoWifiScanTask, weak_factory_.GetWeakPtr()), diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common.h b/chromium/content/browser/geolocation/wifi_data_provider_common.h index e4bed573201..c42b4c2a851 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_common.h +++ b/chromium/content/browser/geolocation/wifi_data_provider_common.h @@ -11,8 +11,8 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" -#include "base/threading/thread.h" -#include "content/browser/geolocation/device_data_provider.h" +#include "content/browser/geolocation/wifi_data_provider.h" +#include "content/browser/geolocation/wifi_polling_policy.h" #include "content/common/content_export.h" namespace content { @@ -20,55 +20,12 @@ namespace content { // Converts a MAC address stored as an array of uint8 to a string. string16 MacAddressAsString16(const uint8 mac_as_int[6]); -// Allows sharing and mocking of the update polling policy function. -class PollingPolicyInterface { - public: - virtual ~PollingPolicyInterface() {} - // Calculates the new polling interval for wiFi scans, given the previous - // interval and whether the last scan produced new results. - virtual void UpdatePollingInterval(bool scan_results_differ) = 0; - virtual int PollingInterval() = 0; - virtual int NoWifiInterval() = 0; -}; - -// Generic polling policy, constants are compile-time parameterized to allow -// tuning on a per-platform basis. -template<int DEFAULT_INTERVAL, - int NO_CHANGE_INTERVAL, - int TWO_NO_CHANGE_INTERVAL, - int NO_WIFI_INTERVAL> -class GenericPollingPolicy : public PollingPolicyInterface { - public: - GenericPollingPolicy() : polling_interval_(DEFAULT_INTERVAL) {} - // PollingPolicyInterface - virtual void UpdatePollingInterval(bool scan_results_differ) { - if (scan_results_differ) { - polling_interval_ = DEFAULT_INTERVAL; - } else if (polling_interval_ == DEFAULT_INTERVAL) { - polling_interval_ = NO_CHANGE_INTERVAL; - } else { - DCHECK(polling_interval_ == NO_CHANGE_INTERVAL || - polling_interval_ == TWO_NO_CHANGE_INTERVAL); - polling_interval_ = TWO_NO_CHANGE_INTERVAL; - } - } - virtual int PollingInterval() { return polling_interval_; } - virtual int NoWifiInterval() { return NO_WIFI_INTERVAL; } - - private: - int polling_interval_; -}; - // Base class to promote code sharing between platform specific wifi data // providers. It's optional for specific platforms to derive this, but if they -// do threading and polling is taken care of by this base class, and all the -// platform need do is provide the underlying WLAN access API and policy policy, -// both of which will be create & accessed in the worker thread (only). -// Also designed this way to promotes ease of testing the cross-platform -// behavior w.r.t. polling & threading. -class CONTENT_EXPORT WifiDataProviderCommon - : public WifiDataProviderImplBase, - private base::Thread { +// do polling behavior is taken care of by this base class, and all the platform +// need do is provide the underlying WLAN access API and polling policy. +// Also designed this way for ease of testing the cross-platform behavior. +class CONTENT_EXPORT WifiDataProviderCommon : public WifiDataProviderImplBase { public: // Interface to abstract the low level data OS library call, and to allow // mocking (hence public). @@ -82,44 +39,38 @@ class CONTENT_EXPORT WifiDataProviderCommon WifiDataProviderCommon(); // WifiDataProviderImplBase implementation - virtual bool StartDataProvider() OVERRIDE; + virtual void StartDataProvider() OVERRIDE; virtual void StopDataProvider() OVERRIDE; virtual bool GetData(WifiData* data) OVERRIDE; protected: virtual ~WifiDataProviderCommon(); - // Returns ownership. Will be called from the worker thread. + // Returns ownership. virtual WlanApiInterface* NewWlanApi() = 0; - // Returns ownership. Will be called from the worker thread. - virtual PollingPolicyInterface* NewPollingPolicy() = 0; + // Returns ownership. + virtual WifiPollingPolicy* NewPollingPolicy() = 0; private: - // Thread implementation - virtual void Init() OVERRIDE; - virtual void CleanUp() OVERRIDE; - - // Task which run in the child thread. + // Runs a scan. Calls the callbacks if new data is found. void DoWifiScanTask(); // Will schedule a scan; i.e. enqueue DoWifiScanTask deferred task. void ScheduleNextScan(int interval); WifiData wifi_data_; - base::Lock data_mutex_; - // Whether we've successfully completed a scan for WiFi data (or the polling - // thread has terminated early). + // Whether we've successfully completed a scan for WiFi data. bool is_first_scan_complete_; // Underlying OS wifi API. scoped_ptr<WlanApiInterface> wlan_api_; // Controls the polling update interval. - scoped_ptr<PollingPolicyInterface> polling_policy_; + scoped_ptr<WifiPollingPolicy> polling_policy_; - // Holder for the tasks which run on the thread; takes care of cleanup. + // Holder for delayed tasks; takes care of cleanup. base::WeakPtrFactory<WifiDataProviderCommon> weak_factory_; DISALLOW_COPY_AND_ASSIGN(WifiDataProviderCommon); diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc b/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc index 9d1b9fbcc7b..61e236f98a8 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_common_unittest.cc @@ -42,7 +42,7 @@ class MockWlanApi : public WifiDataProviderCommon::WlanApiInterface { } }; -class MockPollingPolicy :public PollingPolicyInterface { +class MockPollingPolicy : public WifiPollingPolicy { public: MockPollingPolicy() { ON_CALL(*this,PollingInterval()) @@ -57,27 +57,25 @@ class MockPollingPolicy :public PollingPolicyInterface { virtual void UpdatePollingInterval(bool) {} }; -// Stops the specified (nested) message loop when the listener is called back. -class MessageLoopQuitListener - : public WifiDataProviderCommon::ListenerInterface { +// Stops the specified (nested) message loop when the callback is called. +class MessageLoopQuitter { public: - explicit MessageLoopQuitListener(base::MessageLoop* message_loop) - : message_loop_to_quit_(message_loop) { + explicit MessageLoopQuitter(base::MessageLoop* message_loop) + : message_loop_to_quit_(message_loop), + callback_(base::Bind(&MessageLoopQuitter::WifiDataUpdateAvailable, + base::Unretained(this))) { CHECK(message_loop_to_quit_ != NULL); } - // ListenerInterface - virtual void DeviceDataUpdateAvailable( - DeviceDataProvider<WifiData>* provider) OVERRIDE { + + void WifiDataUpdateAvailable(WifiDataProvider* provider) { // Provider should call back on client's thread. EXPECT_EQ(base::MessageLoop::current(), message_loop_to_quit_); - provider_ = provider; message_loop_to_quit_->QuitNow(); } base::MessageLoop* message_loop_to_quit_; - DeviceDataProvider<WifiData>* provider_; + WifiDataProvider::WifiDataUpdateCallback callback_; }; - class WifiDataProviderCommonWithMock : public WifiDataProviderCommon { public: WifiDataProviderCommonWithMock() @@ -89,7 +87,7 @@ class WifiDataProviderCommonWithMock : public WifiDataProviderCommon { CHECK(new_wlan_api_ != NULL); return new_wlan_api_.release(); } - virtual PollingPolicyInterface* NewPollingPolicy() OVERRIDE { + virtual WifiPollingPolicy* NewPollingPolicy() OVERRIDE { CHECK(new_polling_policy_ != NULL); return new_polling_policy_.release(); } @@ -111,24 +109,24 @@ WifiDataProviderImplBase* CreateWifiDataProviderCommonWithMock() { class GeolocationWifiDataProviderCommonTest : public testing::Test { public: GeolocationWifiDataProviderCommonTest() - : quit_listener_(&main_message_loop_) { + : loop_quitter_(&main_message_loop_) { } virtual void SetUp() { provider_ = new WifiDataProviderCommonWithMock; wlan_api_ = provider_->new_wlan_api_.get(); polling_policy_ = provider_->new_polling_policy_.get(); - provider_->AddListener(&quit_listener_); + provider_->AddCallback(&loop_quitter_.callback_); } virtual void TearDown() { - provider_->RemoveListener(&quit_listener_); + provider_->RemoveCallback(&loop_quitter_.callback_); provider_->StopDataProvider(); provider_ = NULL; } protected: base::MessageLoop main_message_loop_; - MessageLoopQuitListener quit_listener_; + MessageLoopQuitter loop_quitter_; scoped_refptr<WifiDataProviderCommonWithMock> provider_; MockWlanApi* wlan_api_; MockPollingPolicy* polling_policy_; @@ -141,12 +139,12 @@ TEST_F(GeolocationWifiDataProviderCommonTest, CreateDestroy) { EXPECT_TRUE(NULL != wlan_api_); } -TEST_F(GeolocationWifiDataProviderCommonTest, StartThread) { +TEST_F(GeolocationWifiDataProviderCommonTest, RunNormal) { EXPECT_CALL(*wlan_api_, GetAccessPointData(_)) .Times(AtLeast(1)); EXPECT_CALL(*polling_policy_, PollingInterval()) .Times(AtLeast(1)); - EXPECT_TRUE(provider_->StartDataProvider()); + provider_->StartDataProvider(); main_message_loop_.Run(); SUCCEED(); } @@ -188,11 +186,9 @@ TEST_F(GeolocationWifiDataProviderCommonTest, DoAnEmptyScan) { .Times(AtLeast(1)); EXPECT_CALL(*polling_policy_, PollingInterval()) .Times(AtLeast(1)); - EXPECT_TRUE(provider_->StartDataProvider()); + provider_->StartDataProvider(); main_message_loop_.Run(); - // Check we had at least one call. The worker thread may have raced ahead - // and made multiple calls. - EXPECT_GT(wlan_api_->calls_, 0); + EXPECT_EQ(wlan_api_->calls_, 1); WifiData data; EXPECT_TRUE(provider_->GetData(&data)); EXPECT_EQ(0, static_cast<int>(data.access_point_data.size())); @@ -211,23 +207,22 @@ TEST_F(GeolocationWifiDataProviderCommonTest, DoScanWithResults) { single_access_point.ssid = ASCIIToUTF16("foossid"); wlan_api_->data_out_.insert(single_access_point); - EXPECT_TRUE(provider_->StartDataProvider()); + provider_->StartDataProvider(); main_message_loop_.Run(); - EXPECT_GT(wlan_api_->calls_, 0); + EXPECT_EQ(wlan_api_->calls_, 1); WifiData data; EXPECT_TRUE(provider_->GetData(&data)); EXPECT_EQ(1, static_cast<int>(data.access_point_data.size())); EXPECT_EQ(single_access_point.ssid, data.access_point_data.begin()->ssid); } -TEST_F(GeolocationWifiDataProviderCommonTest, - StartThreadViaDeviceDataProvider) { - MessageLoopQuitListener quit_listener(&main_message_loop_); +TEST_F(GeolocationWifiDataProviderCommonTest, RegisterUnregister) { + MessageLoopQuitter loop_quitter(&main_message_loop_); WifiDataProvider::SetFactory(CreateWifiDataProviderCommonWithMock); - DeviceDataProvider<WifiData>::Register(&quit_listener); + WifiDataProvider::Register(&loop_quitter.callback_); main_message_loop_.Run(); - DeviceDataProvider<WifiData>::Unregister(&quit_listener); - DeviceDataProvider<WifiData>::ResetFactory(); + WifiDataProvider::Unregister(&loop_quitter.callback_); + WifiDataProvider::ResetFactory(); } } // namespace content diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc b/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc index 9ac7848835d..a0d334f0c44 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_common_win.cc @@ -7,7 +7,6 @@ #include <assert.h> #include "base/strings/utf_string_conversions.h" -#include "content/browser/geolocation/device_data_provider.h" #include "content/browser/geolocation/wifi_data_provider_common.h" namespace content { diff --git a/chromium/content/browser/geolocation/wifi_data_provider_common_win.h b/chromium/content/browser/geolocation/wifi_data_provider_common_win.h index f58e0c6d2b2..24973a57feb 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_common_win.h +++ b/chromium/content/browser/geolocation/wifi_data_provider_common_win.h @@ -8,7 +8,7 @@ #include <windows.h> #include <ntddndis.h> -#include "content/browser/geolocation/device_data_provider.h" +#include "content/browser/geolocation/wifi_data_provider.h" namespace content { diff --git a/chromium/content/browser/geolocation/wifi_data_provider_linux.cc b/chromium/content/browser/geolocation/wifi_data_provider_linux.cc index edece1d0211..a8d2135fc92 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_linux.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_linux.cc @@ -344,7 +344,6 @@ scoped_ptr<dbus::Response> NetworkManagerWlanApi::GetAccessPointProperty( } // namespace // static -template<> WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { return new WifiDataProviderLinux(); } @@ -363,11 +362,11 @@ WifiDataProviderLinux::NewWlanApi() { return NULL; } -PollingPolicyInterface* WifiDataProviderLinux::NewPollingPolicy() { - return new GenericPollingPolicy<kDefaultPollingIntervalMilliseconds, - kNoChangePollingIntervalMilliseconds, - kTwoNoChangePollingIntervalMilliseconds, - kNoWifiPollingIntervalMilliseconds>; +WifiPollingPolicy* WifiDataProviderLinux::NewPollingPolicy() { + return new GenericWifiPollingPolicy<kDefaultPollingIntervalMilliseconds, + kNoChangePollingIntervalMilliseconds, + kTwoNoChangePollingIntervalMilliseconds, + kNoWifiPollingIntervalMilliseconds>; } WifiDataProviderCommon::WlanApiInterface* diff --git a/chromium/content/browser/geolocation/wifi_data_provider_linux.h b/chromium/content/browser/geolocation/wifi_data_provider_linux.h index 526f61074af..40f6f14e75f 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_linux.h +++ b/chromium/content/browser/geolocation/wifi_data_provider_linux.h @@ -26,7 +26,7 @@ class CONTENT_EXPORT WifiDataProviderLinux : public WifiDataProviderCommon { // WifiDataProviderCommon virtual WlanApiInterface* NewWlanApi() OVERRIDE; - virtual PollingPolicyInterface* NewPollingPolicy() OVERRIDE; + virtual WifiPollingPolicy* NewPollingPolicy() OVERRIDE; // For testing. WlanApiInterface* NewWlanApiForTesting(dbus::Bus* bus); diff --git a/chromium/content/browser/geolocation/wifi_data_provider_mac.cc b/chromium/content/browser/geolocation/wifi_data_provider_mac.cc index 8f81305ac3a..e7d8e8247f9 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_mac.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_mac.cc @@ -158,7 +158,6 @@ bool Apple80211Api::GetAccessPointData(WifiData::AccessPointDataSet* data) { } // namespace // static -template<> WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { return new MacWifiDataProvider(); } @@ -185,11 +184,11 @@ MacWifiDataProvider::WlanApiInterface* MacWifiDataProvider::NewWlanApi() { return NULL; } -PollingPolicyInterface* MacWifiDataProvider::NewPollingPolicy() { - return new GenericPollingPolicy<kDefaultPollingInterval, - kNoChangePollingInterval, - kTwoNoChangePollingInterval, - kNoWifiPollingIntervalMilliseconds>; +WifiPollingPolicy* MacWifiDataProvider::NewPollingPolicy() { + return new GenericWifiPollingPolicy<kDefaultPollingInterval, + kNoChangePollingInterval, + kTwoNoChangePollingInterval, + kNoWifiPollingIntervalMilliseconds>; } } // namespace content diff --git a/chromium/content/browser/geolocation/wifi_data_provider_mac.h b/chromium/content/browser/geolocation/wifi_data_provider_mac.h index e36bd8ca584..ebb0030df2f 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_mac.h +++ b/chromium/content/browser/geolocation/wifi_data_provider_mac.h @@ -21,7 +21,7 @@ class MacWifiDataProvider : public WifiDataProviderCommon { // WifiDataProviderCommon virtual WlanApiInterface* NewWlanApi() OVERRIDE; - virtual PollingPolicyInterface* NewPollingPolicy() OVERRIDE; + virtual WifiPollingPolicy* NewPollingPolicy() OVERRIDE; DISALLOW_COPY_AND_ASSIGN(MacWifiDataProvider); }; diff --git a/chromium/content/browser/geolocation/wifi_data_provider_win.cc b/chromium/content/browser/geolocation/wifi_data_provider_win.cc index a35e6e948f6..8efb1918450 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_win.cc +++ b/chromium/content/browser/geolocation/wifi_data_provider_win.cc @@ -158,7 +158,6 @@ bool ResizeBuffer(int requested_size, scoped_ptr_malloc<BYTE>* buffer); bool GetSystemDirectory(string16* path); } // namespace -template<> WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { return new Win32WifiDataProvider(); } @@ -179,11 +178,11 @@ WifiDataProviderCommon::WlanApiInterface* Win32WifiDataProvider::NewWlanApi() { return WindowsNdisApi::Create(); } -PollingPolicyInterface* Win32WifiDataProvider::NewPollingPolicy() { - return new GenericPollingPolicy<kDefaultPollingInterval, - kNoChangePollingInterval, - kTwoNoChangePollingInterval, - kNoWifiPollingIntervalMilliseconds>; +WifiPollingPolicy* Win32WifiDataProvider::NewPollingPolicy() { + return new GenericWifiPollingPolicy<kDefaultPollingInterval, + kNoChangePollingInterval, + kTwoNoChangePollingInterval, + kNoWifiPollingIntervalMilliseconds>; } // Local classes and functions diff --git a/chromium/content/browser/geolocation/wifi_data_provider_win.h b/chromium/content/browser/geolocation/wifi_data_provider_win.h index 3fbb9cec99d..c39bd580a79 100644 --- a/chromium/content/browser/geolocation/wifi_data_provider_win.h +++ b/chromium/content/browser/geolocation/wifi_data_provider_win.h @@ -9,7 +9,6 @@ #include "content/common/content_export.h" namespace content { -class PollingPolicyInterface; class CONTENT_EXPORT Win32WifiDataProvider : public WifiDataProviderCommon { public: @@ -20,7 +19,7 @@ class CONTENT_EXPORT Win32WifiDataProvider : public WifiDataProviderCommon { // WifiDataProviderCommon virtual WlanApiInterface* NewWlanApi(); - virtual PollingPolicyInterface* NewPollingPolicy(); + virtual WifiPollingPolicy* NewPollingPolicy(); DISALLOW_COPY_AND_ASSIGN(Win32WifiDataProvider); }; diff --git a/chromium/content/browser/geolocation/wifi_polling_policy.h b/chromium/content/browser/geolocation/wifi_polling_policy.h new file mode 100644 index 00000000000..c1df4d267ac --- /dev/null +++ b/chromium/content/browser/geolocation/wifi_polling_policy.h @@ -0,0 +1,55 @@ +// Copyright 2013 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_BROWSER_GEOLOCATION_WIFI_POLLING_POLICY_H_ +#define CONTENT_BROWSER_GEOLOCATION_WIFI_POLLING_POLICY_H_ + +namespace content { + +// Allows sharing and mocking of the update polling policy function. +class WifiPollingPolicy { + public: + WifiPollingPolicy() {} + virtual ~WifiPollingPolicy() {} + // Calculates the new polling interval for wiFi scans, given the previous + // interval and whether the last scan produced new results. + virtual void UpdatePollingInterval(bool scan_results_differ) = 0; + virtual int PollingInterval() = 0; + virtual int NoWifiInterval() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(WifiPollingPolicy); +}; + +// Generic polling policy, constants are compile-time parameterized to allow +// tuning on a per-platform basis. +template<int DEFAULT_INTERVAL, + int NO_CHANGE_INTERVAL, + int TWO_NO_CHANGE_INTERVAL, + int NO_WIFI_INTERVAL> +class GenericWifiPollingPolicy : public WifiPollingPolicy { + public: + GenericWifiPollingPolicy() : polling_interval_(DEFAULT_INTERVAL) {} + // WifiPollingPolicy + virtual void UpdatePollingInterval(bool scan_results_differ) { + if (scan_results_differ) { + polling_interval_ = DEFAULT_INTERVAL; + } else if (polling_interval_ == DEFAULT_INTERVAL) { + polling_interval_ = NO_CHANGE_INTERVAL; + } else { + DCHECK(polling_interval_ == NO_CHANGE_INTERVAL || + polling_interval_ == TWO_NO_CHANGE_INTERVAL); + polling_interval_ = TWO_NO_CHANGE_INTERVAL; + } + } + virtual int PollingInterval() { return polling_interval_; } + virtual int NoWifiInterval() { return NO_WIFI_INTERVAL; } + + private: + int polling_interval_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GEOLOCATION_WIFI_POLLING_POLICY_H_ diff --git a/chromium/content/browser/gpu/compositor_util.cc b/chromium/content/browser/gpu/compositor_util.cc index a0cf1e21a2b..16810e326f3 100644 --- a/chromium/content/browser/gpu/compositor_util.cc +++ b/chromium/content/browser/gpu/compositor_util.cc @@ -2,15 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/public/browser/compositor_util.h" +#include "content/browser/gpu/compositor_util.h" #include "base/command_line.h" #include "base/metrics/field_trial.h" +#include "build/build_config.h" #include "content/public/browser/gpu_data_manager.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" #include "gpu/config/gpu_feature_type.h" +#if defined(OS_MACOSX) +#include "base/mac/mac_util.h" +#elif defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + namespace content { namespace { @@ -44,31 +51,22 @@ bool IsForceCompositingModeBlacklisted() { } // namespace bool IsThreadedCompositingEnabled() { -#if defined(OS_WIN) && defined(USE_AURA) - // We always want compositing on Aura Windows. +#if defined(USE_AURA) + // We always want threaded compositing on Aura. return true; #endif - if (!CanDoAcceleratedCompositing()) - return false; - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); // Command line switches take precedence over blacklist and field trials. if (command_line.HasSwitch(switches::kDisableForceCompositingMode) || - command_line.HasSwitch(switches::kDisableThreadedCompositing)) + command_line.HasSwitch(switches::kDisableThreadedCompositing)) { return false; - -#if defined(OS_CHROMEOS) - // We always want threaded compositing on ChromeOS unless it's explicitly - // disabled above. - return true; -#endif - - if (command_line.HasSwitch(switches::kEnableThreadedCompositing)) + } else if (command_line.HasSwitch(switches::kEnableThreadedCompositing)) { return true; + } - if (IsForceCompositingModeBlacklisted()) + if (!CanDoAcceleratedCompositing() || IsForceCompositingModeBlacklisted()) return false; base::FieldTrial* trial = @@ -78,40 +76,68 @@ bool IsThreadedCompositingEnabled() { } bool IsForceCompositingModeEnabled() { -#if defined(OS_WIN) && defined(USE_AURA) - // We always want compositing on Aura Windows. - return true; -#endif - - if (!CanDoAcceleratedCompositing()) - return false; + // Force compositing mode is a subset of threaded compositing mode. + if (IsThreadedCompositingEnabled()) + return true; const CommandLine& command_line = *CommandLine::ForCurrentProcess(); // Command line switches take precedence over blacklisting and field trials. if (command_line.HasSwitch(switches::kDisableForceCompositingMode)) return false; + else if (command_line.HasSwitch(switches::kForceCompositingMode)) + return true; -#if defined(OS_CHROMEOS) - // We always want compositing ChromeOS unless it's explicitly disabled above. - return true; + if (!CanDoAcceleratedCompositing() || IsForceCompositingModeBlacklisted()) + return false; + + // Hardcode some platforms to use FCM, this has to be done here instead of via + // the field trial so that this configuration is used on try bots as well. + // TODO(gab): Do the same thing in IsThreadedCompositingEnabled() once this is + // stable. + // TODO(gab): Use the GPU blacklist instead of hardcoding OS versions here + // https://codereview.chromium.org/23534006. +#if defined(OS_MACOSX) + // Mac OSX 10.8+ has been shipping with FCM enabled at 100% since M28. + return base::mac::IsOSMountainLionOrLater(); +#elif defined(OS_WIN) + // Windows Vista+ has been shipping with FCM enabled at 100% since M24. + return base::win::GetVersion() >= base::win::VERSION_VISTA; #endif - if (command_line.HasSwitch(switches::kForceCompositingMode)) - return true; + return false; +} - if (IsForceCompositingModeBlacklisted()) - return false; +bool IsDelegatedRendererEnabled() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + bool enabled = false; - base::FieldTrial* trial = - base::FieldTrialList::Find(kGpuCompositingFieldTrialName); + // Flags override. + enabled |= command_line.HasSwitch(switches::kEnableDelegatedRenderer); + enabled &= !command_line.HasSwitch(switches::kDisableDelegatedRenderer); - // Force compositing is enabled in both the force compositing - // and threaded compositing mode field trials. - return trial && - (trial->group_name() == - kGpuCompositingFieldTrialForceCompositingEnabledName || - trial->group_name() == kGpuCompositingFieldTrialThreadEnabledName); + // Needs compositing, and thread. + if (enabled && + (!IsForceCompositingModeEnabled() || !IsThreadedCompositingEnabled())) { + enabled = false; + LOG(ERROR) << "Disabling delegated-rendering because it needs " + << "force-compositing-mode and threaded-compositing."; + } + + return enabled; +} + +bool IsDeadlineSchedulingEnabled() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + + // Default to disabled. + bool enabled = false; + + // Flags override. + enabled |= command_line.HasSwitch(switches::kEnableDeadlineScheduling); + enabled &= !command_line.HasSwitch(switches::kDisableDeadlineScheduling); + + return enabled; } } // namespace content diff --git a/chromium/content/browser/gpu/compositor_util.h b/chromium/content/browser/gpu/compositor_util.h new file mode 100644 index 00000000000..c1c6a4229b1 --- /dev/null +++ b/chromium/content/browser/gpu/compositor_util.h @@ -0,0 +1,26 @@ +// Copyright 2013 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_BROWSER_GPU_COMPOSITOR_UTIL_H_ +#define CONTENT_BROWSER_GPU_COMPOSITOR_UTIL_H_ + +#include "content/common/content_export.h" + +namespace content { + +// Returns true if the threaded compositor is on (via flags or field trial). +CONTENT_EXPORT bool IsThreadedCompositingEnabled(); + +// Returns true if force-compositing-mode is on (via flags or field trial). +CONTENT_EXPORT bool IsForceCompositingModeEnabled(); + +// Returns true if delegated-renderer is on (via flags, or platform default). +CONTENT_EXPORT bool IsDelegatedRendererEnabled(); + +// Returns true if deadline scheduling is on (via flags, or platform default). +CONTENT_EXPORT bool IsDeadlineSchedulingEnabled(); + +} // namespace content + +#endif // CONTENT_BROWSER_GPU_COMPOSITOR_UTIL_H_ diff --git a/chromium/content/browser/gpu/compositor_util_browsertest.cc b/chromium/content/browser/gpu/compositor_util_browsertest.cc new file mode 100644 index 00000000000..75fc60e429f --- /dev/null +++ b/chromium/content/browser/gpu/compositor_util_browsertest.cc @@ -0,0 +1,41 @@ +// Copyright 2013 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/browser/gpu/compositor_util.h" +#include "content/test/content_browser_test.h" + +#if defined(OS_MACOSX) +#include "base/mac/mac_util.h" +#elif defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +namespace content { + +typedef ContentBrowserTest CompositorUtilTest; + +// Test that threaded compositing and FCM are in the expected mode on the bots +// for all platforms. +IN_PROC_BROWSER_TEST_F(CompositorUtilTest, CompositingModeAsExpected) { + enum CompositingMode { + DISABLED, + ENABLED, + THREADED, + } expected_mode = DISABLED; +#if defined(OS_ANDROID) || defined(USE_AURA) + expected_mode = THREADED; +#elif defined(OS_MACOSX) + if (base::mac::IsOSMountainLionOrLater()) + expected_mode = ENABLED; +#elif defined(OS_WIN) + if (base::win::GetVersion() >= base::win::VERSION_VISTA) + expected_mode = ENABLED; +#endif + + EXPECT_EQ(expected_mode == ENABLED || expected_mode == THREADED, + IsForceCompositingModeEnabled()); + EXPECT_EQ(expected_mode == THREADED, IsThreadedCompositingEnabled()); +} + +} diff --git a/chromium/content/browser/gpu/gpu_crash_browsertest.cc b/chromium/content/browser/gpu/gpu_crash_browsertest.cc index eafbc689cd0..98650d320c1 100644 --- a/chromium/content/browser/gpu/gpu_crash_browsertest.cc +++ b/chromium/content/browser/gpu/gpu_crash_browsertest.cc @@ -10,7 +10,7 @@ #include "content/public/common/content_paths.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl.cc b/chromium/content/browser/gpu/gpu_data_manager_impl.cc index 747503cedfe..31096574f9e 100644 --- a/chromium/content/browser/gpu/gpu_data_manager_impl.cc +++ b/chromium/content/browser/gpu/gpu_data_manager_impl.cc @@ -29,6 +29,11 @@ bool GpuDataManagerImpl::IsFeatureBlacklisted(int feature) const { return private_->IsFeatureBlacklisted(feature); } +bool GpuDataManagerImpl::IsDriverBugWorkaroundActive(int feature) const { + base::AutoLock auto_lock(lock_); + return private_->IsDriverBugWorkaroundActive(feature); +} + gpu::GPUInfo GpuDataManagerImpl::GetGPUInfo() const { base::AutoLock auto_lock(lock_); return private_->GetGPUInfo(); @@ -112,6 +117,11 @@ void GpuDataManagerImpl::DisableHardwareAcceleration() { private_->DisableHardwareAcceleration(); } +bool GpuDataManagerImpl::CanUseGpuBrowserCompositor() const { + base::AutoLock auto_lock(lock_); + return private_->CanUseGpuBrowserCompositor(); +} + void GpuDataManagerImpl::Initialize() { base::AutoLock auto_lock(lock_); private_->Initialize(); @@ -208,11 +218,6 @@ bool GpuDataManagerImpl::IsUsingAcceleratedSurface() const { } #endif -bool GpuDataManagerImpl::CanUseGpuBrowserCompositor() const { - base::AutoLock auto_lock(lock_); - return private_->CanUseGpuBrowserCompositor(); -} - void GpuDataManagerImpl::BlockDomainFrom3DAPIs( const GURL& url, DomainGuilt guilt) { base::AutoLock auto_lock(lock_); diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl.h b/chromium/content/browser/gpu/gpu_data_manager_impl.h index 497d3bdf40b..a1ea2013129 100644 --- a/chromium/content/browser/gpu/gpu_data_manager_impl.h +++ b/chromium/content/browser/gpu/gpu_data_manager_impl.h @@ -80,6 +80,7 @@ class CONTENT_EXPORT GpuDataManagerImpl std::string* gl_renderer, std::string* gl_version) OVERRIDE; virtual void DisableHardwareAcceleration() OVERRIDE; + virtual bool CanUseGpuBrowserCompositor() const OVERRIDE; // This collects preliminary GPU info, load GpuBlacklist, and compute the // preliminary blacklisted features; it should only be called at browser @@ -141,8 +142,6 @@ class CONTENT_EXPORT GpuDataManagerImpl bool IsUsingAcceleratedSurface() const; #endif - bool CanUseGpuBrowserCompositor() const; - // Maintenance of domains requiring explicit user permission before // using client-facing 3D APIs (WebGL, Pepper 3D), either because // the domain has caused the GPU to reset, or because too many GPU @@ -177,6 +176,8 @@ class CONTENT_EXPORT GpuDataManagerImpl // Called when GPU process initialization failed. void OnGpuProcessInitFailure(); + bool IsDriverBugWorkaroundActive(int feature) const; + private: friend class GpuDataManagerImplPrivate; friend class GpuDataManagerImplPrivateTest; diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc b/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc index 654048847ec..94adacd798a 100644 --- a/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc +++ b/chromium/content/browser/gpu/gpu_data_manager_impl_private.cc @@ -42,7 +42,6 @@ #include "base/win/windows_version.h" #endif // OS_WIN #if defined(OS_ANDROID) -#include "base/android/build_info.h" #include "ui/gfx/android/device_display_info.h" #endif // OS_ANDROID @@ -230,7 +229,7 @@ std::string IntSetToString(const std::set<int>& list) { void DisplayReconfigCallback(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void* gpu_data_manager) { - if(flags == kCGDisplayBeginConfigurationFlag) + if (flags == kCGDisplayBeginConfigurationFlag) return; // This call contains no information about the display change GpuDataManagerImpl* manager = @@ -259,36 +258,11 @@ void ApplyAndroidWorkarounds(const gpu::GPUInfo& gpu_info, std::string renderer(StringToLowerASCII(gpu_info.gl_renderer)); bool is_img = gpu_info.gl_vendor.find("Imagination") != std::string::npos; - bool is_arm = - gpu_info.gl_vendor.find("ARM") != std::string::npos; - bool is_qualcomm = - gpu_info.gl_vendor.find("Qualcomm") != std::string::npos; - bool is_broadcom = - gpu_info.gl_vendor.find("Broadcom") != std::string::npos; - bool is_mali_t604 = is_arm && - gpu_info.gl_renderer.find("Mali-T604") != std::string::npos; - bool is_nvidia = - gpu_info.gl_vendor.find("NVIDIA") != std::string::npos; - - bool is_vivante = - gpu_info.gl_extensions.find("GL_VIV_shader_binary") != - std::string::npos; - bool is_nexus7 = gpu_info.machine_model.find("Nexus 7") != std::string::npos; bool is_nexus10 = gpu_info.machine_model.find("Nexus 10") != std::string::npos; - int sdk_int = base::android::BuildInfo::GetInstance()->sdk_int(); - - // IMG: avoid context switching perf problems, crashes with share groups - // Mali-T604: http://crbug.com/154715 - // QualComm, NVIDIA: Crashes with share groups - if (is_vivante || is_img || is_mali_t604 || (is_nvidia && (sdk_int < 18)) || - is_qualcomm || is_broadcom) { - command_line->AppendSwitch(switches::kEnableVirtualGLContexts); - } - gfx::DeviceDisplayInfo info; int default_tile_size = 256; @@ -311,7 +285,7 @@ void ApplyAndroidWorkarounds(const gpu::GPUInfo& gpu_info, // If we are using the MapImage API double the tile size to reduce // the number of zero-copy buffers being used. - if (command_line->HasSwitch(cc::switches::kUseMapImage)) + if (command_line->HasSwitch(cc::switches::kEnableMapImage)) default_tile_size *= 2; // Set the command line if it isn't already set and we changed @@ -362,6 +336,13 @@ void GpuDataManagerImplPrivate::InitializeForTesting( } bool GpuDataManagerImplPrivate::IsFeatureBlacklisted(int feature) const { +#if defined(OS_CHROMEOS) + if (feature == gpu::GPU_FEATURE_TYPE_PANEL_FITTING && + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisablePanelFitting)) { + return true; + } +#endif // OS_CHROMEOS if (use_swiftshader_) { // Skia's software rendering is probably more efficient than going through // software emulation of the GPU, so use that. @@ -373,6 +354,10 @@ bool GpuDataManagerImplPrivate::IsFeatureBlacklisted(int feature) const { return (blacklisted_features_.count(feature) == 1); } +bool GpuDataManagerImplPrivate::IsDriverBugWorkaroundActive(int feature) const { + return (gpu_driver_bugs_.count(feature) == 1); +} + size_t GpuDataManagerImplPrivate::GetBlacklistedFeatureCount() const { if (use_swiftshader_) return 1; @@ -740,6 +725,11 @@ void GpuDataManagerImplPrivate::AppendGpuCommandLine( IntSetToString(gpu_driver_bugs_)); } + if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) && + !command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) { + command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode); + } + #if defined(OS_WIN) // DisplayLink 7.1 and earlier can cause the GPU process to crash on startup. // http://crbug.com/177611 @@ -809,8 +799,9 @@ void GpuDataManagerImplPrivate::UpdateRendererWebPrefs( prefs->flash_stage3d_baseline_enabled = false; if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)) prefs->accelerated_2d_canvas_enabled = false; - if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_MULTISAMPLING) - || display_count_ > 1) + if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_MULTISAMPLING) || + (IsDriverBugWorkaroundActive(gpu::DISABLE_MULTIMONITOR_MULTISAMPLING) && + display_count_ > 1)) prefs->gl_multisampling_enabled = false; if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_3D_CSS)) { prefs->accelerated_compositing_for_3d_transforms_enabled = false; @@ -833,6 +824,7 @@ void GpuDataManagerImplPrivate::UpdateRendererWebPrefs( prefs->accelerated_compositing_enabled = true; prefs->accelerated_compositing_for_3d_transforms_enabled = true; prefs->accelerated_compositing_for_plugins_enabled = true; + prefs->accelerated_compositing_for_video_enabled = true; } #if defined(USE_AURA) @@ -1040,21 +1032,24 @@ void GpuDataManagerImplPrivate::InitializeImpl( if (!gpu_blacklist_json.empty()) { gpu_blacklist_.reset(gpu::GpuBlacklist::Create()); - gpu_blacklist_->LoadList( + bool success = gpu_blacklist_->LoadList( browser_version_string, gpu_blacklist_json, gpu::GpuControlList::kCurrentOsOnly); + DCHECK(success); } if (!gpu_switching_list_json.empty()) { gpu_switching_list_.reset(gpu::GpuSwitchingList::Create()); - gpu_switching_list_->LoadList( + bool success = gpu_switching_list_->LoadList( browser_version_string, gpu_switching_list_json, gpu::GpuControlList::kCurrentOsOnly); + DCHECK(success); } if (!gpu_driver_bug_list_json.empty()) { gpu_driver_bug_list_.reset(gpu::GpuDriverBugList::Create()); - gpu_driver_bug_list_->LoadList( + bool success = gpu_driver_bug_list_->LoadList( browser_version_string, gpu_driver_bug_list_json, gpu::GpuControlList::kCurrentOsOnly); + DCHECK(success); } gpu_info_ = gpu_info; @@ -1250,4 +1245,3 @@ void GpuDataManagerImplPrivate::OnGpuProcessInitFailure() { } } // namespace content - diff --git a/chromium/content/browser/gpu/gpu_data_manager_impl_private.h b/chromium/content/browser/gpu/gpu_data_manager_impl_private.h index eb226e863dc..8d15296e4a9 100644 --- a/chromium/content/browser/gpu/gpu_data_manager_impl_private.h +++ b/chromium/content/browser/gpu/gpu_data_manager_impl_private.h @@ -28,6 +28,7 @@ class CONTENT_EXPORT GpuDataManagerImplPrivate { const std::string& gpu_blacklist_json, const gpu::GPUInfo& gpu_info); bool IsFeatureBlacklisted(int feature) const; + bool IsDriverBugWorkaroundActive(int feature) const; gpu::GPUInfo GetGPUInfo() const; void GetGpuProcessHandles( const GpuDataManager::GetGpuProcessHandlesCallback& callback) const; diff --git a/chromium/content/browser/gpu/gpu_functional_browsertest.cc b/chromium/content/browser/gpu/gpu_functional_browsertest.cc index b3c2a57319f..4e95d2d5c11 100644 --- a/chromium/content/browser/gpu/gpu_functional_browsertest.cc +++ b/chromium/content/browser/gpu/gpu_functional_browsertest.cc @@ -10,7 +10,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" @@ -70,7 +70,6 @@ class GpuFunctionalTest : public ContentBrowserTest { } void VerifyGPUProcessOnPage(std::string filename, bool wait) { - Shell::Initialize(); ASSERT_TRUE(test_server()->Start()); DOMMessageQueue message_queue; diff --git a/chromium/content/browser/gpu/gpu_internals_ui.cc b/chromium/content/browser/gpu/gpu_internals_ui.cc index 0e778c277b9..0340077147d 100644 --- a/chromium/content/browser/gpu/gpu_internals_ui.cc +++ b/chromium/content/browser/gpu/gpu_internals_ui.cc @@ -15,9 +15,9 @@ #include "base/sys_info.h" #include "base/values.h" #include "cc/base/switches.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/compositor_util.h" #include "content/public/browser/gpu_data_manager_observer.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" diff --git a/chromium/content/browser/gpu/gpu_memory_test.cc b/chromium/content/browser/gpu/gpu_memory_test.cc index cb74a629299..7f9a3fee167 100644 --- a/chromium/content/browser/gpu/gpu_memory_test.cc +++ b/chromium/content/browser/gpu/gpu_memory_test.cc @@ -12,7 +12,7 @@ #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "gpu/command_buffer/service/gpu_switches.h" @@ -76,7 +76,6 @@ class GpuMemoryTest : public ContentBrowserTest { virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { command_line->AppendSwitch(switches::kEnableLogging); - command_line->AppendSwitch(switches::kForceCompositingMode); command_line->AppendSwitchASCII(switches::kForceGpuMemAvailableMb, kMemoryLimitMBSwitch); // Only run this on GPU bots for now. These tests should work with @@ -217,15 +216,18 @@ class GpuMemoryTest : public ContentBrowserTest { #if defined(OS_LINUX) && !defined(NDEBUG) // http://crbug.com/254724 -#define IF_NOT_DEBUG_LINUX(x) DISABLED_ ## x +#define MAYBE(x) DISABLED_ ## x +#elif defined(OS_WIN) && defined(USE_AURA) +// http://crbug.com/292882 +#define MAYBE(x) DISABLED_ ## x #else -#define IF_NOT_DEBUG_LINUX(x) x +#define MAYBE(x) x #endif // When trying to load something that doesn't fit into our total GPU memory // limit, we shouldn't exceed that limit. IN_PROC_BROWSER_TEST_F(GpuMemoryTest, - IF_NOT_DEBUG_LINUX(SingleWindowDoesNotExceedLimit)) { + MAYBE(SingleWindowDoesNotExceedLimit)) { if (!AllowTestsToRun()) return; diff --git a/chromium/content/browser/gpu/gpu_pixel_browsertest.cc b/chromium/content/browser/gpu/gpu_pixel_browsertest.cc index 5909321377f..d6b55e2c7ee 100644 --- a/chromium/content/browser/gpu/gpu_pixel_browsertest.cc +++ b/chromium/content/browser/gpu/gpu_pixel_browsertest.cc @@ -16,7 +16,7 @@ #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "gpu/config/gpu_test_config.h" @@ -59,7 +59,7 @@ bool ReadPNGFile(const base::FilePath& file_path, SkBitmap* bitmap) { return false; std::string png_data; - return file_util::ReadFileToString(abs_path, &png_data) && + return base::ReadFileToString(abs_path, &png_data) && gfx::PNGCodec::Decode(reinterpret_cast<unsigned char*>(&png_data[0]), png_data.length(), bitmap); @@ -461,7 +461,7 @@ IN_PROC_BROWSER_TEST_F(GpuPixelBrowserTest, MANUAL_WebGLGreenTriangle) { IN_PROC_BROWSER_TEST_F(GpuPixelBrowserTest, MANUAL_CSS3DBlueBox) { // If test baseline needs to be updated after a given revision, update the // following number. If no revision requirement, then 0. - const int64 ref_img_revision_update = 209827; + const int64 ref_img_revision_update = 223891; const ReferencePixel ref_pixels[] = { // x, y, r, g, b @@ -484,7 +484,7 @@ IN_PROC_BROWSER_TEST_F(GpuPixelBrowserTest, MANUAL_CSS3DBlueBox) { IN_PROC_BROWSER_TEST_F(GpuPixelBrowserTest, MANUAL_Canvas2DRedBoxHD) { // If test baseline needs to be updated after a given revision, update the // following number. If no revision requirement, then 0. - const int64 ref_img_revision_update = 123489; + const int64 ref_img_revision_update = 224170; const ReferencePixel ref_pixels[] = { // x, y, r, g, b @@ -513,7 +513,7 @@ class GpuPixelTestCanvas2DSD : public GpuPixelBrowserTest { IN_PROC_BROWSER_TEST_F(GpuPixelTestCanvas2DSD, MANUAL_Canvas2DRedBoxSD) { // If test baseline needs to be updated after a given revision, update the // following number. If no revision requirement, then 0. - const int64 ref_img_revision_update = 123489; + const int64 ref_img_revision_update = 224170; const ReferencePixel ref_pixels[] = { // x, y, r, g, b diff --git a/chromium/content/browser/gpu/gpu_process_host.cc b/chromium/content/browser/gpu/gpu_process_host.cc index 8cbb7aa1d82..0482b347e85 100644 --- a/chromium/content/browser/gpu/gpu_process_host.cc +++ b/chromium/content/browser/gpu/gpu_process_host.cc @@ -6,8 +6,9 @@ #include "base/base64.h" #include "base/base_switches.h" +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/logging.h" @@ -24,8 +25,6 @@ #include "content/common/child_process_host_impl.h" #include "content/common/gpu/gpu_messages.h" #include "content/common/view_messages.h" -#include "content/gpu/gpu_child_thread.h" -#include "content/gpu/gpu_process.h" #include "content/port/browser/render_widget_host_view_frame_subscriber.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" @@ -37,7 +36,7 @@ #include "gpu/command_buffer/service/gpu_switches.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_switches.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #include "ui/gl/gl_switches.h" @@ -81,9 +80,12 @@ void SendGpuProcessMessage(GpuProcessHost::GpuProcessKind kind, } } -void AcceleratedSurfaceBuffersSwappedCompletedForGPU(int host_id, - int route_id, - bool alive) { +void AcceleratedSurfaceBuffersSwappedCompletedForGPU( + int host_id, + int route_id, + bool alive, + base::TimeTicks vsync_timebase, + base::TimeDelta vsync_interval) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( BrowserThread::IO, @@ -91,7 +93,9 @@ void AcceleratedSurfaceBuffersSwappedCompletedForGPU(int host_id, base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU, host_id, route_id, - alive)); + alive, + vsync_timebase, + vsync_interval)); return; } @@ -100,6 +104,10 @@ void AcceleratedSurfaceBuffersSwappedCompletedForGPU(int host_id, if (alive) { AcceleratedSurfaceMsg_BufferPresented_Params ack_params; ack_params.sync_point = 0; +#if defined(OS_WIN) + ack_params.vsync_timebase = vsync_timebase; + ack_params.vsync_interval = vsync_interval; +#endif host->Send( new AcceleratedSurfaceMsg_BufferPresented(route_id, ack_params)); } else { @@ -151,10 +159,10 @@ void AcceleratedSurfaceBuffersSwappedCompleted( base::TimeTicks timebase, base::TimeDelta interval, const ui::LatencyInfo& latency_info) { - AcceleratedSurfaceBuffersSwappedCompletedForGPU(host_id, route_id, - alive); - AcceleratedSurfaceBuffersSwappedCompletedForRenderer(surface_id, timebase, - interval, latency_info); + AcceleratedSurfaceBuffersSwappedCompletedForGPU( + host_id, route_id, alive, timebase, interval); + AcceleratedSurfaceBuffersSwappedCompletedForRenderer( + surface_id, timebase, interval, latency_info); } // NOTE: changes to this class need to be reviewed by the security team. @@ -278,43 +286,6 @@ class GpuSandboxedProcessLauncherDelegate } // anonymous namespace -// Single process not supported in multiple dll mode currently. -#if !defined(CHROME_MULTIPLE_DLL) -// This class creates a GPU thread (instead of a GPU process), when running -// with --in-process-gpu or --single-process. -class GpuMainThread : public base::Thread { - public: - explicit GpuMainThread(const std::string& channel_id) - : base::Thread("Chrome_InProcGpuThread"), - channel_id_(channel_id), - gpu_process_(NULL) { - } - - virtual ~GpuMainThread() { - Stop(); - } - - protected: - virtual void Init() OVERRIDE { - gpu_process_ = new GpuProcess(); - // The process object takes ownership of the thread object, so do not - // save and delete the pointer. - gpu_process_->set_main_thread(new GpuChildThread(channel_id_)); - } - - virtual void CleanUp() OVERRIDE { - delete gpu_process_; - } - - private: - std::string channel_id_; - // Deleted in CleanUp() on the gpu thread, so don't use smart pointers. - GpuProcess* gpu_process_; - - DISALLOW_COPY_AND_ASSIGN(GpuMainThread); -}; -#endif // !CHROME_MULTIPLE_DLL - // static bool GpuProcessHost::ValidateHost(GpuProcessHost* host) { if (!host) @@ -401,6 +372,13 @@ void GpuProcessHost::SendOnIO(GpuProcessKind kind, } } +GpuMainThreadFactoryFunction g_gpu_main_thread_factory = NULL; + +void GpuProcessHost::RegisterGpuMainThreadFactory( + GpuMainThreadFactoryFunction create) { + g_gpu_main_thread_factory = create; +} + // static GpuProcessHost* GpuProcessHost::FromID(int host_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -550,7 +528,8 @@ GpuProcessHost::~GpuProcessHost() { std::string message; if (!in_process_) { int exit_code; - base::TerminationStatus status = process_->GetTerminationStatus(&exit_code); + base::TerminationStatus status = process_->GetTerminationStatus( + false /* known_dead */, &exit_code); UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessTerminationStatus", status, base::TERMINATION_STATUS_MAX_ENUM); @@ -598,19 +577,15 @@ bool GpuProcessHost::Init() { if (channel_id.empty()) return false; - // Single process not supported in multiple dll mode currently. -#if !defined(CHROME_MULTIPLE_DLL) - if (in_process_) { + if (in_process_ && g_gpu_main_thread_factory) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kDisableGpuWatchdog); - in_process_gpu_thread_.reset(new GpuMainThread(channel_id)); + in_process_gpu_thread_.reset(g_gpu_main_thread_factory(channel_id)); in_process_gpu_thread_->Start(); OnProcessLaunched(); // Fake a callback that the process is ready. - } else -#endif // !CHROME_MULTIPLE_DLL - if (!LaunchGpuProcess(channel_id)) { + } else if (!LaunchGpuProcess(channel_id)) { return false; } @@ -916,7 +891,7 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( base::ScopedClosureRunner scoped_completion_runner( base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU, host_id_, params.route_id, - true /* alive */)); + true /* alive */, base::TimeTicks(), base::TimeDelta())); int render_process_id = 0; int render_widget_id = 0; @@ -933,7 +908,7 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( // if the browser is waiting for a new frame. Otherwise the RenderWidgetHelper // will forward to the RenderWidgetHostView via RenderProcessHostImpl and // RenderWidgetHostImpl. - scoped_completion_runner.Release(); + ignore_result(scoped_completion_runner.Release()); ViewHostMsg_CompositorSurfaceBuffersSwapped_Params view_params; view_params.surface_id = params.surface_id; @@ -969,7 +944,7 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( TRACE_EVENT1("gpu", "SurfaceIDNotFound_RoutingToUI", "surface_id", params.surface_id); // This is a content area swap, send it on to the UI thread. - scoped_completion_runner.Release(); + ignore_result(scoped_completion_runner.Release()); RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params)); return; } @@ -983,7 +958,7 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( "EarlyOut_NativeWindowNotFound", "handle", handle.handle); - scoped_completion_runner.Release(); + ignore_result(scoped_completion_runner.Release()); AcceleratedSurfaceBuffersSwappedCompleted(host_id_, params.route_id, params.surface_id, @@ -994,7 +969,7 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( return; } - scoped_completion_runner.Release(); + ignore_result(scoped_completion_runner.Release()); presenter->AsyncPresentAndAcknowledge( params.size, params.surface_handle, @@ -1079,7 +1054,7 @@ void GpuProcessHost::OnProcessLaunched() { void GpuProcessHost::OnProcessCrashed(int exit_code) { SendOutstandingReplies(); GpuDataManagerImpl::GetInstance()->ProcessCrashed( - process_->GetTerminationStatus(NULL)); + process_->GetTerminationStatus(true /* known_dead */, NULL)); } GpuProcessHost::GpuProcessKind GpuProcessHost::kind() { @@ -1148,7 +1123,6 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) { switches::kDisableSeccompFilterSandbox, switches::kEnableLogging, switches::kEnableShareGroupAsyncTextureUpload, - switches::kEnableVirtualGLContexts, switches::kGpuStartupDialog, switches::kGpuSandboxAllowSysVShm, switches::kLoggingLevel, @@ -1195,7 +1169,7 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) { new GpuSandboxedProcessLauncherDelegate(cmd_line), #elif defined(OS_POSIX) false, - base::EnvironmentVector(), + base::EnvironmentMap(), #endif cmd_line); process_launched_ = true; diff --git a/chromium/content/browser/gpu/gpu_process_host.h b/chromium/content/browser/gpu/gpu_process_host.h index 8908a1035db..32345b72800 100644 --- a/chromium/content/browser/gpu/gpu_process_host.h +++ b/chromium/content/browser/gpu/gpu_process_host.h @@ -44,6 +44,8 @@ class GpuMainThread; class RenderWidgetHostViewFrameSubscriber; class ShaderDiskCache; +typedef base::Thread* (*GpuMainThreadFactoryFunction)(const std::string& id); + class GpuProcessHost : public BrowserChildProcessHostDelegate, public IPC::Sender, public base::NonThreadSafe { @@ -82,6 +84,9 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate, CauseForGpuLaunch cause, IPC::Message* message); + CONTENT_EXPORT static void RegisterGpuMainThreadFactory( + GpuMainThreadFactoryFunction create); + // Get the GPU process host for the GPU process with the given ID. Returns // null if the process no longer exists. static GpuProcessHost* FromID(int host_id); @@ -214,9 +219,7 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate, bool swiftshader_rendering_; GpuProcessKind kind_; -#if !defined(CHROME_MULTIPLE_DLL) - scoped_ptr<GpuMainThread> in_process_gpu_thread_; -#endif + scoped_ptr<base::Thread> in_process_gpu_thread_; // Whether we actually launched a GPU process. bool process_launched_; diff --git a/chromium/content/browser/gpu/webgl_conformance_test.cc b/chromium/content/browser/gpu/webgl_conformance_test.cc index 79f30782830..e8fb8a59b92 100644 --- a/chromium/content/browser/gpu/webgl_conformance_test.cc +++ b/chromium/content/browser/gpu/webgl_conformance_test.cc @@ -10,7 +10,7 @@ #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "gpu/config/gpu_test_config.h" diff --git a/chromium/content/browser/indexed_db/indexed_db_backing_store.cc b/chromium/content/browser/indexed_db/indexed_db_backing_store.cc index 58369110014..70b3b52105b 100644 --- a/chromium/content/browser/indexed_db/indexed_db_backing_store.cc +++ b/chromium/content/browser/indexed_db/indexed_db_backing_store.cc @@ -348,11 +348,10 @@ WARN_UNUSED_RESULT static bool GetMaxObjectStoreId( class DefaultLevelDBFactory : public LevelDBFactory { public: - virtual leveldb::Status OpenLevelDB( - const base::FilePath& file_name, - const LevelDBComparator* comparator, - scoped_ptr<LevelDBDatabase>* db, - bool* is_disk_full) OVERRIDE { + virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name, + const LevelDBComparator* comparator, + scoped_ptr<LevelDBDatabase>* db, + bool* is_disk_full) OVERRIDE { return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full); } virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE { @@ -406,6 +405,7 @@ enum IndexedDBLevelDBBackingStoreOpenResult { INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, }; +// TODO(dgrogan): Move to leveldb_env. bool RecoveryCouldBeFruitful(leveldb::Status status) { leveldb_env::MethodID method; int error = -1; @@ -445,13 +445,15 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( const std::string& origin_identifier, const base::FilePath& path_base, const std::string& file_identifier, - WebKit::WebIDBCallbacks::DataLoss* data_loss) { + WebKit::WebIDBCallbacks::DataLoss* data_loss, + bool* disk_full) { *data_loss = WebKit::WebIDBCallbacks::DataLossNone; DefaultLevelDBFactory leveldb_factory; return IndexedDBBackingStore::Open(origin_identifier, path_base, file_identifier, data_loss, + disk_full, &leveldb_factory); } @@ -460,10 +462,12 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( const base::FilePath& path_base, const std::string& file_identifier, WebKit::WebIDBCallbacks::DataLoss* data_loss, + bool* is_disk_full, LevelDBFactory* leveldb_factory) { IDB_TRACE("IndexedDBBackingStore::Open"); DCHECK(!path_base.empty()); *data_loss = WebKit::WebIDBCallbacks::DataLossNone; + *is_disk_full = false; scoped_ptr<LevelDBComparator> comparator(new Comparator()); @@ -529,10 +533,14 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( base::FilePath file_path = path_base.Append(identifier_path); - bool is_disk_full = false; scoped_ptr<LevelDBDatabase> db; leveldb::Status status = leveldb_factory->OpenLevelDB( - file_path, comparator.get(), &db, &is_disk_full); + file_path, comparator.get(), &db, is_disk_full); + + if (!status.ok() && leveldb_env::IndicatesDiskFull(status)) { + DCHECK(!db); + *is_disk_full = true; + } if (db) { bool known = false; @@ -569,24 +577,24 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, base::HistogramBase::kUmaTargetedHistogramFlag) ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_SUCCESS); - } else if (is_disk_full) { - LOG(ERROR) << "Unable to open backing store - disk is full."; + } else if (!RecoveryCouldBeFruitful(status)) { + LOG(ERROR) << "Unable to open backing store, not trying to recover - " + << status.ToString(); base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", 1, INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_DISK_FULL); + ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_NO_RECOVERY); return scoped_refptr<IndexedDBBackingStore>(); - } else if (!RecoveryCouldBeFruitful(status)) { - LOG(ERROR) << "Unable to open backing store, not trying to recover - " - << status.ToString(); + } else if (*is_disk_full) { + LOG(ERROR) << "Unable to open backing store - disk is full."; base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus", 1, INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX, INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1, base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_NO_RECOVERY); + ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_DISK_FULL); return scoped_refptr<IndexedDBBackingStore>(); } else { LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup"; @@ -968,10 +976,10 @@ bool IndexedDBBackingStore::GetObjectStores( it->Next(); if (!CheckObjectStoreAndMetaDataType( - it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::AUTO_INCREMENT)) { + it.get(), + stop_key, + object_store_id, + ObjectStoreMetaDataKey::AUTO_INCREMENT)) { INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); break; } @@ -993,20 +1001,20 @@ bool IndexedDBBackingStore::GetObjectStores( it->Next(); // Last version. if (!CheckObjectStoreAndMetaDataType( - it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::LAST_VERSION)) { + it.get(), + stop_key, + object_store_id, + ObjectStoreMetaDataKey::LAST_VERSION)) { INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); break; } it->Next(); // Maximum index id allocated. if (!CheckObjectStoreAndMetaDataType( - it.get(), - stop_key, - object_store_id, - ObjectStoreMetaDataKey::MAX_INDEX_ID)) { + it.get(), + stop_key, + object_store_id, + ObjectStoreMetaDataKey::MAX_INDEX_ID)) { INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES); break; } @@ -1542,7 +1550,7 @@ bool IndexedDBBackingStore::GetIndexes( it->Next(); // unique flag if (!CheckIndexAndMetaDataKey( - it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) { + it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) { INTERNAL_CONSISTENCY_ERROR(GET_INDEXES); break; } @@ -1555,7 +1563,7 @@ bool IndexedDBBackingStore::GetIndexes( it->Next(); // key_path if (!CheckIndexAndMetaDataKey( - it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) { + it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) { INTERNAL_CONSISTENCY_ERROR(GET_INDEXES); break; } @@ -1623,7 +1631,7 @@ bool IndexedDBBackingStore::CreateIndex( LevelDBTransaction* leveldb_transaction = IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction); if (!SetMaxIndexId( - leveldb_transaction, database_id, object_store_id, index_id)) + leveldb_transaction, database_id, object_store_id, index_id)) return false; const std::string name_key = IndexMetaDataKey::Encode( @@ -2402,8 +2410,9 @@ bool ObjectStoreCursorOptions( cursor_options->high_open = true; // Not included. } else { // We need a key that exists. - if (!FindGreatestKeyLessThanOrEqual( - transaction, cursor_options->high_key, &cursor_options->high_key)) + if (!FindGreatestKeyLessThanOrEqual(transaction, + cursor_options->high_key, + &cursor_options->high_key)) return false; cursor_options->high_open = false; } @@ -2416,7 +2425,7 @@ bool ObjectStoreCursorOptions( // For reverse cursors, we need a key that exists. std::string found_high_key; if (!FindGreatestKeyLessThanOrEqual( - transaction, cursor_options->high_key, &found_high_key)) + transaction, cursor_options->high_key, &found_high_key)) return false; // If the target key should not be included, but we end up with a smaller @@ -2472,8 +2481,9 @@ bool IndexCursorOptions( cursor_options->high_open = false; // Included. if (!cursor_options->forward) { // We need a key that exists. - if (!FindGreatestKeyLessThanOrEqual( - transaction, cursor_options->high_key, &cursor_options->high_key)) + if (!FindGreatestKeyLessThanOrEqual(transaction, + cursor_options->high_key, + &cursor_options->high_key)) return false; cursor_options->high_open = false; } @@ -2485,7 +2495,7 @@ bool IndexCursorOptions( std::string found_high_key; // Seek to the *last* key in the set of non-unique keys if (!FindGreatestKeyLessThanOrEqual( - transaction, cursor_options->high_key, &found_high_key)) + transaction, cursor_options->high_key, &found_high_key)) return false; // If the target key should not be included, but we end up with a smaller diff --git a/chromium/content/browser/indexed_db/indexed_db_backing_store.h b/chromium/content/browser/indexed_db/indexed_db_backing_store.h index 4498b0875a4..3948411de24 100644 --- a/chromium/content/browser/indexed_db/indexed_db_backing_store.h +++ b/chromium/content/browser/indexed_db/indexed_db_backing_store.h @@ -22,6 +22,7 @@ #include "content/common/indexed_db/indexed_db_key_path.h" #include "content/common/indexed_db/indexed_db_key_range.h" #include "third_party/WebKit/public/platform/WebIDBCallbacks.h" +#include "third_party/leveldatabase/src/include/leveldb/status.h" namespace content { @@ -48,13 +49,15 @@ class CONTENT_EXPORT IndexedDBBackingStore const std::string& origin_identifier, const base::FilePath& path_base, const std::string& file_identifier, - WebKit::WebIDBCallbacks::DataLoss* data_loss); + WebKit::WebIDBCallbacks::DataLoss* data_loss, + bool* disk_full); static scoped_refptr<IndexedDBBackingStore> Open( const std::string& origin_identifier, const base::FilePath& path_base, const std::string& file_identifier, WebKit::WebIDBCallbacks::DataLoss* data_loss, + bool* disk_full, LevelDBFactory* factory); static scoped_refptr<IndexedDBBackingStore> OpenInMemory( const std::string& file_identifier); diff --git a/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc index cc6ea1f2a64..50fb2f07a5d 100644 --- a/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc +++ b/chromium/content/browser/indexed_db/indexed_db_backing_store_unittest.cc @@ -12,6 +12,7 @@ #include "content/browser/indexed_db/indexed_db_factory.h" #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" #include "third_party/WebKit/public/platform/WebIDBTypes.h" #include "url/gurl.h" #include "webkit/common/database/database_identifier.h" @@ -325,10 +326,12 @@ class MockIDBFactory : public IndexedDBFactory { const base::FilePath& data_directory) { WebKit::WebIDBCallbacks::DataLoss data_loss = WebKit::WebIDBCallbacks::DataLossNone; + bool disk_full; scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore(webkit_database::GetIdentifierFromOrigin(origin), data_directory, - &data_loss); + &data_loss, + &disk_full); EXPECT_EQ(WebKit::WebIDBCallbacks::DataLossNone, data_loss); return backing_store; } @@ -413,6 +416,50 @@ TEST(IndexedDBFactoryTest, RejectLongOrigins) { EXPECT_TRUE(diskStore2); } +class DiskFullFactory : public IndexedDBFactory { + private: + virtual ~DiskFullFactory() {} + virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStore( + const std::string& origin_identifier, + const base::FilePath& data_directory, + WebKit::WebIDBCallbacks::DataLoss* data_loss, + bool* disk_full) OVERRIDE { + *disk_full = true; + return scoped_refptr<IndexedDBBackingStore>(); + } +}; + +class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks { + public: + LookingForQuotaErrorMockCallbacks() + : IndexedDBCallbacks(NULL, 0, 0), error_called_(false) {} + virtual void OnError(const IndexedDBDatabaseError& error) OVERRIDE { + error_called_ = true; + EXPECT_EQ(WebKit::WebIDBDatabaseExceptionQuotaError, error.code()); + } + private: + virtual ~LookingForQuotaErrorMockCallbacks() { + EXPECT_TRUE(error_called_); + } + bool error_called_; +}; + +TEST(IndexedDBFactoryTest, QuotaErrorOnDiskFull) { + scoped_refptr<DiskFullFactory> factory = new DiskFullFactory; + scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks = + new LookingForQuotaErrorMockCallbacks; + scoped_refptr<IndexedDBDatabaseCallbacks> dummy_database_callbacks = + new IndexedDBDatabaseCallbacks(NULL, 0, 0); + const string16 name(ASCIIToUTF16("name")); + factory->Open(name, + 1, /* version */ + 2, /* transaction_id */ + callbacks, + dummy_database_callbacks, + "origin", + base::FilePath(FILE_PATH_LITERAL("/dummy"))); +} + } // namespace } // namespace content diff --git a/chromium/content/browser/indexed_db/indexed_db_browsertest.cc b/chromium/content/browser/indexed_db/indexed_db_browsertest.cc index ab010cbd92a..fb6d0cd2795 100644 --- a/chromium/content/browser/indexed_db/indexed_db_browsertest.cc +++ b/chromium/content/browser/indexed_db/indexed_db_browsertest.cc @@ -21,7 +21,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "webkit/browser/database/database_util.h" @@ -46,7 +46,8 @@ class IndexedDBBrowserTest : public ContentBrowserTest { LOG(INFO) << "Navigating to URL and blocking."; NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); LOG(INFO) << "Navigation done."; - std::string result = the_browser->web_contents()->GetURL().ref(); + std::string result = + the_browser->web_contents()->GetLastCommittedURL().ref(); if (result != "pass") { std::string js_result; ASSERT_TRUE(ExecuteScriptAndExtractString( @@ -430,4 +431,16 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) { EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); } +class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest { + public: + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + command_line->AppendSwitch(switches::kSingleProcess); + } +}; + +IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess, + RenderThreadShutdownTest) { + SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html")); +} + } // namespace content diff --git a/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc index e05ceb91f57..6b60a4f9291 100644 --- a/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc +++ b/chromium/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc @@ -69,11 +69,13 @@ TEST(IndexedDBIOErrorTest, CleanUpTest) { MockLevelDBFactory mock_leveldb_factory; WebKit::WebIDBCallbacks::DataLoss data_loss = WebKit::WebIDBCallbacks::DataLossNone; + bool disk_full = false; scoped_refptr<IndexedDBBackingStore> backing_store = IndexedDBBackingStore::Open(origin_identifier, path, dummy_file_identifier, &data_loss, + &disk_full, &mock_leveldb_factory); } @@ -115,6 +117,7 @@ TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) { std::string dummy_file_identifier; WebKit::WebIDBCallbacks::DataLoss data_loss = WebKit::WebIDBCallbacks::DataLossNone; + bool disk_full = false; MockErrorLevelDBFactory<int> mock_leveldb_factory(ENOSPC, false); scoped_refptr<IndexedDBBackingStore> backing_store = @@ -122,6 +125,7 @@ TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) { path, dummy_file_identifier, &data_loss, + &disk_full, &mock_leveldb_factory); MockErrorLevelDBFactory<base::PlatformFileError> mock_leveldb_factory2( @@ -131,6 +135,7 @@ TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) { path, dummy_file_identifier, &data_loss, + &disk_full, &mock_leveldb_factory2); MockErrorLevelDBFactory<int> mock_leveldb_factory3(EIO, true); @@ -139,6 +144,7 @@ TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) { path, dummy_file_identifier, &data_loss, + &disk_full, &mock_leveldb_factory3); MockErrorLevelDBFactory<base::PlatformFileError> mock_leveldb_factory4( @@ -148,6 +154,7 @@ TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) { path, dummy_file_identifier, &data_loss, + &disk_full, &mock_leveldb_factory4); } diff --git a/chromium/content/browser/indexed_db/indexed_db_context_impl.cc b/chromium/content/browser/indexed_db/indexed_db_context_impl.cc index 38decc286f4..6c4dd84ed79 100644 --- a/chromium/content/browser/indexed_db/indexed_db_context_impl.cc +++ b/chromium/content/browser/indexed_db/indexed_db_context_impl.cc @@ -15,10 +15,12 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" +#include "base/time/time.h" #include "base/values.h" #include "content/browser/browser_main_loop.h" #include "content/browser/indexed_db/indexed_db_connection.h" #include "content/browser/indexed_db/indexed_db_database.h" +#include "content/browser/indexed_db/indexed_db_dispatcher_host.h" #include "content/browser/indexed_db/indexed_db_factory.h" #include "content/browser/indexed_db/indexed_db_quota_client.h" #include "content/browser/indexed_db/indexed_db_transaction.h" @@ -218,7 +220,41 @@ ListValue* IndexedDBContextImpl::GetAllOriginsDetails() { const char* kModes[] = { "readonly", "readwrite", "versionchange" }; transaction_info->SetString("mode", kModes[transaction->mode()]); - transaction_info->SetBoolean("running", transaction->IsRunning()); + switch (transaction->queue_status()) { + case IndexedDBTransaction::CREATED: + transaction_info->SetString("status", "created"); + break; + case IndexedDBTransaction::BLOCKED: + transaction_info->SetString("status", "blocked"); + break; + case IndexedDBTransaction::UNBLOCKED: + if (transaction->IsRunning()) + transaction_info->SetString("status", "running"); + else + transaction_info->SetString("status", "started"); + break; + } + + transaction_info->SetDouble( + "pid", + IndexedDBDispatcherHost::TransactionIdToProcessId( + transaction->id())); + transaction_info->SetDouble( + "tid", + IndexedDBDispatcherHost::TransactionIdToRendererTransactionId( + transaction->id())); + transaction_info->SetDouble( + "age", + (base::Time::Now() - transaction->creation_time()) + .InMillisecondsF()); + transaction_info->SetDouble( + "runtime", + (base::Time::Now() - transaction->start_time()) + .InMillisecondsF()); + transaction_info->SetDouble("tasks_scheduled", + transaction->tasks_scheduled()); + transaction_info->SetDouble("tasks_completed", + transaction->tasks_completed()); scoped_ptr<ListValue> scope(new ListValue()); for (std::set<int64>::const_iterator scope_it = diff --git a/chromium/content/browser/indexed_db/indexed_db_cursor.cc b/chromium/content/browser/indexed_db/indexed_db_cursor.cc index 1ee13e7c0b7..cb254d95908 100644 --- a/chromium/content/browser/indexed_db/indexed_db_cursor.cc +++ b/chromium/content/browser/indexed_db/indexed_db_cursor.cc @@ -4,6 +4,7 @@ #include "content/browser/indexed_db/indexed_db_cursor.h" +#include "base/bind.h" #include "base/logging.h" #include "content/browser/indexed_db/indexed_db_callbacks.h" #include "content/browser/indexed_db/indexed_db_database_error.h" @@ -12,53 +13,6 @@ namespace content { -class IndexedDBCursor::CursorIterationOperation - : public IndexedDBTransaction::Operation { - public: - CursorIterationOperation(scoped_refptr<IndexedDBCursor> cursor, - scoped_ptr<IndexedDBKey> key, - scoped_refptr<IndexedDBCallbacks> callbacks) - : cursor_(cursor), key_(key.Pass()), callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - scoped_refptr<IndexedDBCursor> cursor_; - scoped_ptr<IndexedDBKey> key_; - scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - -class IndexedDBCursor::CursorAdvanceOperation - : public IndexedDBTransaction::Operation { - public: - CursorAdvanceOperation(scoped_refptr<IndexedDBCursor> cursor, - uint32 count, - scoped_refptr<IndexedDBCallbacks> callbacks) - : cursor_(cursor), count_(count), callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - scoped_refptr<IndexedDBCursor> cursor_; - uint32 count_; - scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - -class IndexedDBCursor::CursorPrefetchIterationOperation - : public IndexedDBTransaction::Operation { - public: - CursorPrefetchIterationOperation(scoped_refptr<IndexedDBCursor> cursor, - int number_to_fetch, - scoped_refptr<IndexedDBCallbacks> callbacks) - : cursor_(cursor), - number_to_fetch_(number_to_fetch), - callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - scoped_refptr<IndexedDBCursor> cursor_; - int number_to_fetch_; - scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - IndexedDBCursor::IndexedDBCursor( scoped_ptr<IndexedDBBackingStore::Cursor> cursor, indexed_db::CursorType cursor_type, @@ -81,7 +35,11 @@ void IndexedDBCursor::Continue(scoped_ptr<IndexedDBKey> key, IDB_TRACE("IndexedDBCursor::Continue"); transaction_->ScheduleTask( - task_type_, new CursorIterationOperation(this, key.Pass(), callbacks)); + task_type_, + base::Bind(&IndexedDBCursor::CursorIterationOperation, + this, + base::Passed(&key), + callbacks)); } void IndexedDBCursor::Advance(uint32 count, @@ -89,35 +47,38 @@ void IndexedDBCursor::Advance(uint32 count, IDB_TRACE("IndexedDBCursor::Advance"); transaction_->ScheduleTask( - new CursorAdvanceOperation(this, count, callbacks)); + task_type_, + base::Bind( + &IndexedDBCursor::CursorAdvanceOperation, this, count, callbacks)); } -void IndexedDBCursor::CursorAdvanceOperation::Perform( +void IndexedDBCursor::CursorAdvanceOperation( + uint32 count, + scoped_refptr<IndexedDBCallbacks> callbacks, IndexedDBTransaction* /*transaction*/) { - IDB_TRACE("CursorAdvanceOperation"); - if (!cursor_->cursor_ || !cursor_->cursor_->Advance(count_)) { - cursor_->cursor_.reset(); - callbacks_->OnSuccess(static_cast<std::string*>(NULL)); + IDB_TRACE("IndexedDBCursor::CursorAdvanceOperation"); + if (!cursor_ || !cursor_->Advance(count)) { + cursor_.reset(); + callbacks->OnSuccess(static_cast<std::string*>(NULL)); return; } - callbacks_->OnSuccess( - cursor_->key(), cursor_->primary_key(), cursor_->Value()); + callbacks->OnSuccess(key(), primary_key(), Value()); } -void IndexedDBCursor::CursorIterationOperation::Perform( +void IndexedDBCursor::CursorIterationOperation( + scoped_ptr<IndexedDBKey> key, + scoped_refptr<IndexedDBCallbacks> callbacks, IndexedDBTransaction* /*transaction*/) { - IDB_TRACE("CursorIterationOperation"); - if (!cursor_->cursor_ || - !cursor_->cursor_->Continue(key_.get(), - IndexedDBBackingStore::Cursor::SEEK)) { - cursor_->cursor_.reset(); - callbacks_->OnSuccess(static_cast<std::string*>(NULL)); + IDB_TRACE("IndexedDBCursor::CursorIterationOperation"); + if (!cursor_ || + !cursor_->Continue(key.get(), IndexedDBBackingStore::Cursor::SEEK)) { + cursor_.reset(); + callbacks->OnSuccess(static_cast<std::string*>(NULL)); return; } - callbacks_->OnSuccess( - cursor_->key(), cursor_->primary_key(), cursor_->Value()); + callbacks->OnSuccess(this->key(), primary_key(), Value()); } void IndexedDBCursor::PrefetchContinue( @@ -127,38 +88,43 @@ void IndexedDBCursor::PrefetchContinue( transaction_->ScheduleTask( task_type_, - new CursorPrefetchIterationOperation(this, number_to_fetch, callbacks)); + base::Bind(&IndexedDBCursor::CursorPrefetchIterationOperation, + this, + number_to_fetch, + callbacks)); } -void IndexedDBCursor::CursorPrefetchIterationOperation::Perform( +void IndexedDBCursor::CursorPrefetchIterationOperation( + int number_to_fetch, + scoped_refptr<IndexedDBCallbacks> callbacks, IndexedDBTransaction* /*transaction*/) { - IDB_TRACE("CursorPrefetchIterationOperation"); + IDB_TRACE("IndexedDBCursor::CursorPrefetchIterationOperation"); std::vector<IndexedDBKey> found_keys; std::vector<IndexedDBKey> found_primary_keys; std::vector<std::string> found_values; - if (cursor_->cursor_) - cursor_->saved_cursor_.reset(cursor_->cursor_->Clone()); + if (cursor_) + saved_cursor_.reset(cursor_->Clone()); const size_t max_size_estimate = 10 * 1024 * 1024; size_t size_estimate = 0; - for (int i = 0; i < number_to_fetch_; ++i) { - if (!cursor_->cursor_ || !cursor_->cursor_->Continue()) { - cursor_->cursor_.reset(); + for (int i = 0; i < number_to_fetch; ++i) { + if (!cursor_ || !cursor_->Continue()) { + cursor_.reset(); break; } - found_keys.push_back(cursor_->cursor_->key()); - found_primary_keys.push_back(cursor_->cursor_->primary_key()); + found_keys.push_back(cursor_->key()); + found_primary_keys.push_back(cursor_->primary_key()); - switch (cursor_->cursor_type_) { + switch (cursor_type_) { case indexed_db::CURSOR_KEY_ONLY: found_values.push_back(std::string()); break; case indexed_db::CURSOR_KEY_AND_VALUE: { std::string value; - value.swap(*cursor_->cursor_->Value()); + value.swap(*cursor_->Value()); size_estimate += value.size(); found_values.push_back(value); break; @@ -166,19 +132,19 @@ void IndexedDBCursor::CursorPrefetchIterationOperation::Perform( default: NOTREACHED(); } - size_estimate += cursor_->cursor_->key().size_estimate(); - size_estimate += cursor_->cursor_->primary_key().size_estimate(); + size_estimate += cursor_->key().size_estimate(); + size_estimate += cursor_->primary_key().size_estimate(); if (size_estimate > max_size_estimate) break; } if (!found_keys.size()) { - callbacks_->OnSuccess(static_cast<std::string*>(NULL)); + callbacks->OnSuccess(static_cast<std::string*>(NULL)); return; } - callbacks_->OnSuccessWithPrefetch( + callbacks->OnSuccessWithPrefetch( found_keys, found_primary_keys, found_values); } diff --git a/chromium/content/browser/indexed_db/indexed_db_cursor.h b/chromium/content/browser/indexed_db/indexed_db_cursor.h index 2cf9d4f3ecc..db5963058e7 100644 --- a/chromium/content/browser/indexed_db/indexed_db_cursor.h +++ b/chromium/content/browser/indexed_db/indexed_db_cursor.h @@ -41,15 +41,22 @@ class CONTENT_EXPORT IndexedDBCursor } void Close(); + void CursorIterationOperation(scoped_ptr<IndexedDBKey> key, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction); + void CursorAdvanceOperation(uint32 count, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction); + void CursorPrefetchIterationOperation( + int number_to_fetch, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction); + private: friend class base::RefCounted<IndexedDBCursor>; ~IndexedDBCursor(); - class CursorIterationOperation; - class CursorAdvanceOperation; - class CursorPrefetchIterationOperation; - IndexedDBDatabase::TaskType task_type_; indexed_db::CursorType cursor_type_; const scoped_refptr<IndexedDBTransaction> transaction_; diff --git a/chromium/content/browser/indexed_db/indexed_db_database.cc b/chromium/content/browser/indexed_db/indexed_db_database.cc index 9eb577448df..f7b2a9bcff8 100644 --- a/chromium/content/browser/indexed_db/indexed_db_database.cc +++ b/chromium/content/browser/indexed_db/indexed_db_database.cc @@ -29,345 +29,6 @@ using WebKit::WebIDBKeyTypeNumber; namespace content { -class CreateObjectStoreOperation : public IndexedDBTransaction::Operation { - public: - CreateObjectStoreOperation( - scoped_refptr<IndexedDBBackingStore> backing_store, - const IndexedDBObjectStoreMetadata& object_store_metadata) - : backing_store_(backing_store), - object_store_metadata_(object_store_metadata) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const IndexedDBObjectStoreMetadata object_store_metadata_; -}; - -class DeleteObjectStoreOperation : public IndexedDBTransaction::Operation { - public: - DeleteObjectStoreOperation( - scoped_refptr<IndexedDBBackingStore> backing_store, - const IndexedDBObjectStoreMetadata& object_store_metadata) - : backing_store_(backing_store), - object_store_metadata_(object_store_metadata) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const IndexedDBObjectStoreMetadata object_store_metadata_; -}; - -class IndexedDBDatabase::VersionChangeOperation - : public IndexedDBTransaction::Operation { - public: - VersionChangeOperation(scoped_refptr<IndexedDBDatabase> database, - int64 transaction_id, - int64 version, - scoped_refptr<IndexedDBCallbacks> callbacks, - scoped_ptr<IndexedDBConnection> connection, - WebKit::WebIDBCallbacks::DataLoss data_loss) - : database_(database), - transaction_id_(transaction_id), - version_(version), - callbacks_(callbacks), - connection_(connection.Pass()), - data_loss_(data_loss) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - scoped_refptr<IndexedDBDatabase> database_; - int64 transaction_id_; - int64 version_; - scoped_refptr<IndexedDBCallbacks> callbacks_; - scoped_ptr<IndexedDBConnection> connection_; - WebKit::WebIDBCallbacks::DataLoss data_loss_; -}; - -class CreateObjectStoreAbortOperation : public IndexedDBTransaction::Operation { - public: - CreateObjectStoreAbortOperation(scoped_refptr<IndexedDBDatabase> database, - int64 object_store_id) - : database_(database), object_store_id_(object_store_id) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBDatabase> database_; - const int64 object_store_id_; -}; - -class DeleteObjectStoreAbortOperation : public IndexedDBTransaction::Operation { - public: - DeleteObjectStoreAbortOperation( - scoped_refptr<IndexedDBDatabase> database, - const IndexedDBObjectStoreMetadata& object_store_metadata) - : database_(database), object_store_metadata_(object_store_metadata) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - scoped_refptr<IndexedDBDatabase> database_; - IndexedDBObjectStoreMetadata object_store_metadata_; -}; - -class IndexedDBDatabase::VersionChangeAbortOperation - : public IndexedDBTransaction::Operation { - public: - VersionChangeAbortOperation(scoped_refptr<IndexedDBDatabase> database, - const string16& previous_version, - int64 previous_int_version) - : database_(database), - previous_version_(previous_version), - previous_int_version_(previous_int_version) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - scoped_refptr<IndexedDBDatabase> database_; - string16 previous_version_; - int64 previous_int_version_; -}; - -class CreateIndexOperation : public IndexedDBTransaction::Operation { - public: - CreateIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 object_store_id, - const IndexedDBIndexMetadata& index_metadata) - : backing_store_(backing_store), - object_store_id_(object_store_id), - index_metadata_(index_metadata) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 object_store_id_; - const IndexedDBIndexMetadata index_metadata_; -}; - -class DeleteIndexOperation : public IndexedDBTransaction::Operation { - public: - DeleteIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 object_store_id, - const IndexedDBIndexMetadata& index_metadata) - : backing_store_(backing_store), - object_store_id_(object_store_id), - index_metadata_(index_metadata) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 object_store_id_; - const IndexedDBIndexMetadata index_metadata_; -}; - -class CreateIndexAbortOperation : public IndexedDBTransaction::Operation { - public: - CreateIndexAbortOperation(scoped_refptr<IndexedDBDatabase> database, - int64 object_store_id, - int64 index_id) - : database_(database), - object_store_id_(object_store_id), - index_id_(index_id) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBDatabase> database_; - const int64 object_store_id_; - const int64 index_id_; -}; - -class DeleteIndexAbortOperation : public IndexedDBTransaction::Operation { - public: - DeleteIndexAbortOperation(scoped_refptr<IndexedDBDatabase> database, - int64 object_store_id, - const IndexedDBIndexMetadata& index_metadata) - : database_(database), - object_store_id_(object_store_id), - index_metadata_(index_metadata) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBDatabase> database_; - const int64 object_store_id_; - const IndexedDBIndexMetadata index_metadata_; -}; - -class GetOperation : public IndexedDBTransaction::Operation { - public: - GetOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 database_id, - int64 object_store_id, - int64 index_id, - const IndexedDBKeyPath& key_path, - const bool auto_increment, - scoped_ptr<IndexedDBKeyRange> key_range, - indexed_db::CursorType cursor_type, - scoped_refptr<IndexedDBCallbacks> callbacks) - : backing_store_(backing_store), - database_id_(database_id), - object_store_id_(object_store_id), - index_id_(index_id), - key_path_(key_path), - auto_increment_(auto_increment), - key_range_(key_range.Pass()), - cursor_type_(cursor_type), - callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 database_id_; - const int64 object_store_id_; - const int64 index_id_; - const IndexedDBKeyPath key_path_; - const bool auto_increment_; - const scoped_ptr<IndexedDBKeyRange> key_range_; - const indexed_db::CursorType cursor_type_; - const scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - -class PutOperation : public IndexedDBTransaction::Operation { - public: - PutOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 database_id, - const IndexedDBObjectStoreMetadata& object_store, - std::string* value, - scoped_ptr<IndexedDBKey> key, - IndexedDBDatabase::PutMode put_mode, - scoped_refptr<IndexedDBCallbacks> callbacks, - const std::vector<int64>& index_ids, - const std::vector<IndexedDBDatabase::IndexKeys>& index_keys) - : backing_store_(backing_store), - database_id_(database_id), - object_store_(object_store), - key_(key.Pass()), - put_mode_(put_mode), - callbacks_(callbacks), - index_ids_(index_ids), - index_keys_(index_keys) { - value_.swap(*value); - } - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 database_id_; - const IndexedDBObjectStoreMetadata object_store_; - std::string value_; - scoped_ptr<IndexedDBKey> key_; - const IndexedDBDatabase::PutMode put_mode_; - const scoped_refptr<IndexedDBCallbacks> callbacks_; - const std::vector<int64> index_ids_; - const std::vector<IndexedDBDatabase::IndexKeys> index_keys_; -}; - -class SetIndexesReadyOperation : public IndexedDBTransaction::Operation { - public: - explicit SetIndexesReadyOperation(size_t index_count) - : index_count_(index_count) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const size_t index_count_; -}; - -class OpenCursorOperation : public IndexedDBTransaction::Operation { - public: - OpenCursorOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 database_id, - int64 object_store_id, - int64 index_id, - scoped_ptr<IndexedDBKeyRange> key_range, - indexed_db::CursorDirection direction, - indexed_db::CursorType cursor_type, - IndexedDBDatabase::TaskType task_type, - scoped_refptr<IndexedDBCallbacks> callbacks) - : backing_store_(backing_store), - database_id_(database_id), - object_store_id_(object_store_id), - index_id_(index_id), - key_range_(key_range.Pass()), - direction_(direction), - cursor_type_(cursor_type), - task_type_(task_type), - callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 database_id_; - const int64 object_store_id_; - const int64 index_id_; - const scoped_ptr<IndexedDBKeyRange> key_range_; - const indexed_db::CursorDirection direction_; - const indexed_db::CursorType cursor_type_; - const IndexedDBDatabase::TaskType task_type_; - const scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - -class CountOperation : public IndexedDBTransaction::Operation { - public: - CountOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 database_id, - int64 object_store_id, - int64 index_id, - scoped_ptr<IndexedDBKeyRange> key_range, - scoped_refptr<IndexedDBCallbacks> callbacks) - : backing_store_(backing_store), - database_id_(database_id), - object_store_id_(object_store_id), - index_id_(index_id), - key_range_(key_range.Pass()), - callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 database_id_; - const int64 object_store_id_; - const int64 index_id_; - const scoped_ptr<IndexedDBKeyRange> key_range_; - const scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - -class DeleteRangeOperation : public IndexedDBTransaction::Operation { - public: - DeleteRangeOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 database_id, - int64 object_store_id, - scoped_ptr<IndexedDBKeyRange> key_range, - scoped_refptr<IndexedDBCallbacks> callbacks) - : backing_store_(backing_store), - database_id_(database_id), - object_store_id_(object_store_id), - key_range_(key_range.Pass()), - callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 database_id_; - const int64 object_store_id_; - const scoped_ptr<IndexedDBKeyRange> key_range_; - const scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - -class ClearOperation : public IndexedDBTransaction::Operation { - public: - ClearOperation(scoped_refptr<IndexedDBBackingStore> backing_store, - int64 database_id, - int64 object_store_id, - scoped_refptr<IndexedDBCallbacks> callbacks) - : backing_store_(backing_store), - database_id_(database_id), - object_store_id_(object_store_id), - callbacks_(callbacks) {} - virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE; - - private: - const scoped_refptr<IndexedDBBackingStore> backing_store_; - const int64 database_id_; - const int64 object_store_id_; - const scoped_refptr<IndexedDBCallbacks> callbacks_; -}; - // PendingOpenCall has a scoped_refptr<IndexedDBDatabaseCallbacks> because it // isn't a connection yet. class IndexedDBDatabase::PendingOpenCall { @@ -455,14 +116,14 @@ class IndexedDBDatabase::PendingDeleteCall { scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create( const string16& name, - IndexedDBBackingStore* database, + IndexedDBBackingStore* backing_store, IndexedDBFactory* factory, const Identifier& unique_identifier) { - scoped_refptr<IndexedDBDatabase> backend = - new IndexedDBDatabase(name, database, factory, unique_identifier); - if (!backend->OpenInternal()) + scoped_refptr<IndexedDBDatabase> database = + new IndexedDBDatabase(name, backing_store, factory, unique_identifier); + if (!database->OpenInternal()) return 0; - return backend; + return database; } namespace { @@ -474,11 +135,10 @@ bool Contains(const T& container, const U& item) { } } -IndexedDBDatabase::IndexedDBDatabase( - const string16& name, - IndexedDBBackingStore* backing_store, - IndexedDBFactory* factory, - const Identifier& unique_identifier) +IndexedDBDatabase::IndexedDBDatabase(const string16& name, + IndexedDBBackingStore* backing_store, + IndexedDBFactory* factory, + const Identifier& unique_identifier) : backing_store_(backing_store), metadata_(name, kInvalidId, @@ -647,25 +307,31 @@ void IndexedDBDatabase::CreateObjectStore(int64 transaction_id, IndexedDBDatabase::kMinimumIndexId); transaction->ScheduleTask( - new CreateObjectStoreOperation(backing_store_, object_store_metadata), - new CreateObjectStoreAbortOperation(this, object_store_id)); + base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation, + this, + object_store_metadata), + base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation, + this, + object_store_id)); AddObjectStore(object_store_metadata, object_store_id); } -void CreateObjectStoreOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("CreateObjectStoreOperation"); +void IndexedDBDatabase::CreateObjectStoreOperation( + const IndexedDBObjectStoreMetadata& object_store_metadata, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation"); if (!backing_store_->CreateObjectStore( transaction->BackingStoreTransaction(), transaction->database()->id(), - object_store_metadata_.id, - object_store_metadata_.name, - object_store_metadata_.key_path, - object_store_metadata_.auto_increment)) { + object_store_metadata.id, + object_store_metadata.name, + object_store_metadata.key_path, + object_store_metadata.auto_increment)) { transaction->Abort(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, ASCIIToUTF16("Internal error creating object store '") + - object_store_metadata_.name + ASCIIToUTF16("'."))); + object_store_metadata.name + ASCIIToUTF16("'."))); return; } } @@ -685,8 +351,12 @@ void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id, metadata_.object_stores[object_store_id]; transaction->ScheduleTask( - new DeleteObjectStoreOperation(backing_store_, object_store_metadata), - new DeleteObjectStoreAbortOperation(this, object_store_metadata)); + base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation, + this, + object_store_metadata), + base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation, + this, + object_store_metadata)); RemoveObjectStore(object_store_id); } @@ -709,34 +379,46 @@ void IndexedDBDatabase::CreateIndex(int64 transaction_id, name, index_id, key_path, unique, multi_entry); transaction->ScheduleTask( - new CreateIndexOperation(backing_store_, object_store_id, index_metadata), - new CreateIndexAbortOperation(this, object_store_id, index_id)); + base::Bind(&IndexedDBDatabase::CreateIndexOperation, + this, + object_store_id, + index_metadata), + base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation, + this, + object_store_id, + index_id)); AddIndex(object_store_id, index_metadata, index_id); } -void CreateIndexOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("CreateIndexOperation"); +void IndexedDBDatabase::CreateIndexOperation( + int64 object_store_id, + const IndexedDBIndexMetadata& index_metadata, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::CreateIndexOperation"); if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(), transaction->database()->id(), - object_store_id_, - index_metadata_.id, - index_metadata_.name, - index_metadata_.key_path, - index_metadata_.unique, - index_metadata_.multi_entry)) { + object_store_id, + index_metadata.id, + index_metadata.name, + index_metadata.key_path, + index_metadata.unique, + index_metadata.multi_entry)) { string16 error_string = ASCIIToUTF16("Internal error creating index '") + - index_metadata_.name + ASCIIToUTF16("'."); + index_metadata.name + ASCIIToUTF16("'."); transaction->Abort(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, error_string)); return; } } -void CreateIndexAbortOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("CreateIndexAbortOperation"); +void IndexedDBDatabase::CreateIndexAbortOperation( + int64 object_store_id, + int64 index_id, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation"); DCHECK(!transaction); - database_->RemoveIndex(object_store_id_, index_id_); + RemoveIndex(object_store_id, index_id); } void IndexedDBDatabase::DeleteIndex(int64 transaction_id, @@ -754,31 +436,42 @@ void IndexedDBDatabase::DeleteIndex(int64 transaction_id, metadata_.object_stores[object_store_id].indexes[index_id]; transaction->ScheduleTask( - new DeleteIndexOperation(backing_store_, object_store_id, index_metadata), - new DeleteIndexAbortOperation(this, object_store_id, index_metadata)); + base::Bind(&IndexedDBDatabase::DeleteIndexOperation, + this, + object_store_id, + index_metadata), + base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation, + this, + object_store_id, + index_metadata)); RemoveIndex(object_store_id, index_id); } -void DeleteIndexOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("DeleteIndexOperation"); +void IndexedDBDatabase::DeleteIndexOperation( + int64 object_store_id, + const IndexedDBIndexMetadata& index_metadata, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation"); bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(), transaction->database()->id(), - object_store_id_, - index_metadata_.id); + object_store_id, + index_metadata.id); if (!ok) { string16 error_string = ASCIIToUTF16("Internal error deleting index '") + - index_metadata_.name + ASCIIToUTF16("'."); + index_metadata.name + ASCIIToUTF16("'."); transaction->Abort(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, error_string)); } } -void DeleteIndexAbortOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("DeleteIndexAbortOperation"); +void IndexedDBDatabase::DeleteIndexAbortOperation( + int64 object_store_id, + const IndexedDBIndexMetadata& index_metadata, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation"); DCHECK(!transaction); - database_->AddIndex( - object_store_id_, index_metadata_, IndexedDBIndexMetadata::kInvalidId); + AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId); } void IndexedDBDatabase::Commit(int64 transaction_id) { @@ -821,61 +514,68 @@ void IndexedDBDatabase::Get(int64 transaction_id, if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) return; - const IndexedDBObjectStoreMetadata& object_store_metadata = - metadata_.object_stores[object_store_id]; - transaction->ScheduleTask(new GetOperation( - backing_store_, - metadata_.id, + transaction->ScheduleTask(base::Bind( + &IndexedDBDatabase::GetOperation, + this, object_store_id, index_id, - object_store_metadata.key_path, - object_store_metadata.auto_increment, - key_range.Pass(), + Passed(&key_range), key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE, callbacks)); } -void GetOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("GetOperation"); +void IndexedDBDatabase::GetOperation( + int64 object_store_id, + int64 index_id, + scoped_ptr<IndexedDBKeyRange> key_range, + indexed_db::CursorType cursor_type, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::GetOperation"); + + DCHECK(metadata_.object_stores.find(object_store_id) != + metadata_.object_stores.end()); + const IndexedDBObjectStoreMetadata& object_store_metadata = + metadata_.object_stores[object_store_id]; const IndexedDBKey* key; scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; - if (key_range_->IsOnlyKey()) { - key = &key_range_->lower(); + if (key_range->IsOnlyKey()) { + key = &key_range->lower(); } else { - if (index_id_ == IndexedDBIndexMetadata::kInvalidId) { - DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY); + if (index_id == IndexedDBIndexMetadata::kInvalidId) { + DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY); // ObjectStore Retrieval Operation backing_store_cursor = backing_store_->OpenObjectStoreCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - *key_range_, + id(), + object_store_id, + *key_range, indexed_db::CURSOR_NEXT); - } else if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) { + } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { // Index Value Retrieval Operation backing_store_cursor = backing_store_->OpenIndexKeyCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - index_id_, - *key_range_, + id(), + object_store_id, + index_id, + *key_range, indexed_db::CURSOR_NEXT); } else { // Index Referenced Value Retrieval Operation backing_store_cursor = backing_store_->OpenIndexCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - index_id_, - *key_range_, + id(), + object_store_id, + index_id, + *key_range, indexed_db::CURSOR_NEXT); } if (!backing_store_cursor) { - callbacks_->OnSuccess(); + callbacks->OnSuccess(); return; } @@ -884,82 +584,84 @@ void GetOperation::Perform(IndexedDBTransaction* transaction) { scoped_ptr<IndexedDBKey> primary_key; bool ok; - if (index_id_ == IndexedDBIndexMetadata::kInvalidId) { + if (index_id == IndexedDBIndexMetadata::kInvalidId) { // Object Store Retrieval Operation std::string value; ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, + id(), + object_store_id, *key, &value); if (!ok) { - callbacks_->OnError( + callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error in GetRecord.")); return; } if (value.empty()) { - callbacks_->OnSuccess(); + callbacks->OnSuccess(); return; } - if (auto_increment_ && !key_path_.IsNull()) { - callbacks_->OnSuccess(&value, *key, key_path_); + if (object_store_metadata.auto_increment && + !object_store_metadata.key_path.IsNull()) { + callbacks->OnSuccess(&value, *key, object_store_metadata.key_path); return; } - callbacks_->OnSuccess(&value); + callbacks->OnSuccess(&value); return; } // From here we are dealing only with indexes. ok = backing_store_->GetPrimaryKeyViaIndex( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - index_id_, + id(), + object_store_id, + index_id, *key, &primary_key); if (!ok) { - callbacks_->OnError( + callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error in GetPrimaryKeyViaIndex.")); return; } if (!primary_key) { - callbacks_->OnSuccess(); + callbacks->OnSuccess(); return; } - if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) { + if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { // Index Value Retrieval Operation - callbacks_->OnSuccess(*primary_key); + callbacks->OnSuccess(*primary_key); return; } // Index Referenced Value Retrieval Operation std::string value; ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, + id(), + object_store_id, *primary_key, &value); if (!ok) { - callbacks_->OnError( + callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error in GetRecord.")); return; } if (value.empty()) { - callbacks_->OnSuccess(); + callbacks->OnSuccess(); return; } - if (auto_increment_ && !key_path_.IsNull()) { - callbacks_->OnSuccess(&value, *primary_key, key_path_); + if (object_store_metadata.auto_increment && + !object_store_metadata.key_path.IsNull()) { + callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path); return; } - callbacks_->OnSuccess(&value); + callbacks->OnSuccess(&value); } static scoped_ptr<IndexedDBKey> GenerateKey( @@ -982,8 +684,7 @@ static scoped_ptr<IndexedDBKey> GenerateKey( if (current_number < 0 || current_number > max_generator_value) return make_scoped_ptr(new IndexedDBKey()); - return make_scoped_ptr( - new IndexedDBKey(current_number, WebIDBKeyTypeNumber)); + return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber)); } static bool UpdateKeyGenerator( @@ -991,18 +692,30 @@ static bool UpdateKeyGenerator( scoped_refptr<IndexedDBTransaction> transaction, int64 database_id, int64 object_store_id, - const IndexedDBKey* key, + const IndexedDBKey& key, bool check_current) { - DCHECK(key); - DCHECK_EQ(WebIDBKeyTypeNumber, key->type()); + DCHECK_EQ(WebIDBKeyTypeNumber, key.type()); return backing_store->MaybeUpdateKeyGeneratorCurrentNumber( transaction->BackingStoreTransaction(), database_id, object_store_id, - static_cast<int64>(floor(key->number())) + 1, + static_cast<int64>(floor(key.number())) + 1, check_current); } +struct IndexedDBDatabase::PutOperationParams { + PutOperationParams() {} + int64 object_store_id; + std::string value; + scoped_ptr<IndexedDBKey> key; + IndexedDBDatabase::PutMode put_mode; + scoped_refptr<IndexedDBCallbacks> callbacks; + std::vector<int64> index_ids; + std::vector<IndexKeys> index_keys; + + DISALLOW_COPY_AND_ASSIGN(PutOperationParams); +}; + void IndexedDBDatabase::Put(int64 transaction_id, int64 object_store_id, std::string* value, @@ -1019,65 +732,70 @@ void IndexedDBDatabase::Put(int64 transaction_id, if (!ValidateObjectStoreId(object_store_id)) return; - const IndexedDBObjectStoreMetadata& object_store_metadata = - metadata_.object_stores[object_store_id]; DCHECK(key); - DCHECK(object_store_metadata.auto_increment || key->IsValid()); - transaction->ScheduleTask(new PutOperation(backing_store_, - id(), - object_store_metadata, - value, - key.Pass(), - put_mode, - callbacks, - index_ids, - index_keys)); -} - -void PutOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("PutOperation"); + scoped_ptr<PutOperationParams> params(new PutOperationParams()); + params->object_store_id = object_store_id; + params->value.swap(*value); + params->key = key.Pass(); + params->put_mode = put_mode; + params->callbacks = callbacks; + params->index_ids = index_ids; + params->index_keys = index_keys; + transaction->ScheduleTask(base::Bind( + &IndexedDBDatabase::PutOperation, this, base::Passed(¶ms))); +} + +void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::PutOperation"); DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); - DCHECK_EQ(index_ids_.size(), index_keys_.size()); + DCHECK_EQ(params->index_ids.size(), params->index_keys.size()); bool key_was_generated = false; + DCHECK(metadata_.object_stores.find(params->object_store_id) != + metadata_.object_stores.end()); + const IndexedDBObjectStoreMetadata& object_store = + metadata_.object_stores[params->object_store_id]; + DCHECK(object_store.auto_increment || params->key->IsValid()); + scoped_ptr<IndexedDBKey> key; - if (put_mode_ != IndexedDBDatabase::CURSOR_UPDATE && - object_store_.auto_increment && !key_->IsValid()) { - scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey( - backing_store_, transaction, database_id_, object_store_.id); + if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE && + object_store.auto_increment && !params->key->IsValid()) { + scoped_ptr<IndexedDBKey> auto_inc_key = + GenerateKey(backing_store_, transaction, id(), params->object_store_id); key_was_generated = true; if (!auto_inc_key->IsValid()) { - callbacks_->OnError( + params->callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError, "Maximum key generator value reached.")); return; } key = auto_inc_key.Pass(); } else { - key = key_.Pass(); + key = params->key.Pass(); } DCHECK(key->IsValid()); IndexedDBBackingStore::RecordIdentifier record_identifier; - if (put_mode_ == IndexedDBDatabase::ADD_ONLY) { + if (params->put_mode == IndexedDBDatabase::ADD_ONLY) { bool found = false; bool ok = backing_store_->KeyExistsInObjectStore( transaction->BackingStoreTransaction(), - database_id_, - object_store_.id, - *key.get(), + id(), + params->object_store_id, + *key, &record_identifier, &found); if (!ok) { - callbacks_->OnError( + params->callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error checking key existence.")); return; } if (found) { - callbacks_->OnError( + params->callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError, "Key already exists in the object store.")); return; @@ -1088,24 +806,24 @@ void PutOperation::Perform(IndexedDBTransaction* transaction) { string16 error_message; bool obeys_constraints = false; bool backing_store_success = MakeIndexWriters(transaction, - backing_store_, - database_id_, - object_store_, + backing_store_.get(), + id(), + object_store, *key, key_was_generated, - index_ids_, - index_keys_, + params->index_ids, + params->index_keys, &index_writers, &error_message, &obeys_constraints); if (!backing_store_success) { - callbacks_->OnError(IndexedDBDatabaseError( + params->callbacks->OnError(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error: backing store error updating index keys.")); return; } if (!obeys_constraints) { - callbacks_->OnError(IndexedDBDatabaseError( + params->callbacks->OnError(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionConstraintError, error_message)); return; } @@ -1114,13 +832,13 @@ void PutOperation::Perform(IndexedDBTransaction* transaction) { // transaction in case of error. backing_store_success = backing_store_->PutRecord(transaction->BackingStoreTransaction(), - database_id_, - object_store_.id, - *key.get(), - value_, + id(), + params->object_store_id, + *key, + params->value, &record_identifier); if (!backing_store_success) { - callbacks_->OnError(IndexedDBDatabaseError( + params->callbacks->OnError(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error: backing store error performing put/add.")); return; @@ -1129,30 +847,30 @@ void PutOperation::Perform(IndexedDBTransaction* transaction) { for (size_t i = 0; i < index_writers.size(); ++i) { IndexWriter* index_writer = index_writers[i]; index_writer->WriteIndexKeys(record_identifier, - backing_store_, + backing_store_.get(), transaction->BackingStoreTransaction(), - database_id_, - object_store_.id); + id(), + params->object_store_id); } - if (object_store_.auto_increment && - put_mode_ != IndexedDBDatabase::CURSOR_UPDATE && + if (object_store.auto_increment && + params->put_mode != IndexedDBDatabase::CURSOR_UPDATE && key->type() == WebIDBKeyTypeNumber) { bool ok = UpdateKeyGenerator(backing_store_, transaction, - database_id_, - object_store_.id, - key.get(), + id(), + params->object_store_id, + *key, !key_was_generated); if (!ok) { - callbacks_->OnError( + params->callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error updating key generator.")); return; } } - callbacks_->OnSuccess(*key); + params->callbacks->OnSuccess(*key); } void IndexedDBDatabase::SetIndexKeys(int64 transaction_id, @@ -1240,16 +958,34 @@ void IndexedDBDatabase::SetIndexesReady(int64 transaction_id, return; DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); - transaction->ScheduleTask(IndexedDBDatabase::PREEMPTIVE_TASK, - new SetIndexesReadyOperation(index_ids.size())); + transaction->ScheduleTask( + IndexedDBDatabase::PREEMPTIVE_TASK, + base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation, + this, + index_ids.size())); } -void SetIndexesReadyOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("SetIndexesReadyOperation"); - for (size_t i = 0; i < index_count_; ++i) +void IndexedDBDatabase::SetIndexesReadyOperation( + size_t index_count, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation"); + for (size_t i = 0; i < index_count; ++i) transaction->DidCompletePreemptiveEvent(); } +struct IndexedDBDatabase::OpenCursorOperationParams { + OpenCursorOperationParams() {} + int64 object_store_id; + int64 index_id; + scoped_ptr<IndexedDBKeyRange> key_range; + indexed_db::CursorDirection direction; + indexed_db::CursorType cursor_type; + IndexedDBDatabase::TaskType task_type; + scoped_refptr<IndexedDBCallbacks> callbacks; + + DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams); +}; + void IndexedDBDatabase::OpenCursor( int64 transaction_id, int64 object_store_id, @@ -1267,66 +1003,81 @@ void IndexedDBDatabase::OpenCursor( if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) return; - transaction->ScheduleTask(new OpenCursorOperation( - backing_store_, - id(), - object_store_id, - index_id, - key_range.Pass(), - direction, - key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE, - task_type, - callbacks)); -} - -void OpenCursorOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("OpenCursorOperation"); + scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams()); + params->object_store_id = object_store_id; + params->index_id = index_id; + params->key_range = key_range.Pass(); + params->direction = direction; + params->cursor_type = + key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE; + params->task_type = task_type; + params->callbacks = callbacks; + transaction->ScheduleTask(base::Bind( + &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(¶ms))); +} + +void IndexedDBDatabase::OpenCursorOperation( + scoped_ptr<OpenCursorOperationParams> params, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::OpenCursorOperation"); // The frontend has begun indexing, so this pauses the transaction // until the indexing is complete. This can't happen any earlier // because we don't want to switch to early mode in case multiple // indexes are being created in a row, with Put()'s in between. - if (task_type_ == IndexedDBDatabase::PREEMPTIVE_TASK) + if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK) transaction->AddPreemptiveEvent(); scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; - if (index_id_ == IndexedDBIndexMetadata::kInvalidId) { - DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY); - backing_store_cursor = backing_store_->OpenObjectStoreCursor( - transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - *key_range_, - direction_); + if (params->index_id == IndexedDBIndexMetadata::kInvalidId) { + if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { + DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK); + backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( + transaction->BackingStoreTransaction(), + id(), + params->object_store_id, + *params->key_range, + params->direction); + } else { + backing_store_cursor = backing_store_->OpenObjectStoreCursor( + transaction->BackingStoreTransaction(), + id(), + params->object_store_id, + *params->key_range, + params->direction); + } } else { - DCHECK_EQ(task_type_, IndexedDBDatabase::NORMAL_TASK); - if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) { + DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK); + if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { backing_store_cursor = backing_store_->OpenIndexKeyCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - index_id_, - *key_range_, - direction_); + id(), + params->object_store_id, + params->index_id, + *params->key_range, + params->direction); } else { backing_store_cursor = backing_store_->OpenIndexCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - index_id_, - *key_range_, - direction_); + id(), + params->object_store_id, + params->index_id, + *params->key_range, + params->direction); } } if (!backing_store_cursor) { - callbacks_->OnSuccess(static_cast<std::string*>(NULL)); + params->callbacks->OnSuccess(static_cast<std::string*>(NULL)); return; } - scoped_refptr<IndexedDBCursor> cursor = new IndexedDBCursor( - backing_store_cursor.Pass(), cursor_type_, task_type_, transaction); - callbacks_->OnSuccess( + scoped_refptr<IndexedDBCursor> cursor = + new IndexedDBCursor(backing_store_cursor.Pass(), + params->cursor_type, + params->task_type, + transaction); + params->callbacks->OnSuccess( cursor, cursor->key(), cursor->primary_key(), cursor->Value()); } @@ -1343,37 +1094,42 @@ void IndexedDBDatabase::Count(int64 transaction_id, if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) return; - transaction->ScheduleTask(new CountOperation(backing_store_, - id(), - object_store_id, - index_id, - key_range.Pass(), - callbacks)); + transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation, + this, + object_store_id, + index_id, + base::Passed(&key_range), + callbacks)); } -void CountOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("CountOperation"); +void IndexedDBDatabase::CountOperation( + int64 object_store_id, + int64 index_id, + scoped_ptr<IndexedDBKeyRange> key_range, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::CountOperation"); uint32 count = 0; scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; - if (index_id_ == IndexedDBIndexMetadata::kInvalidId) { + if (index_id == IndexedDBIndexMetadata::kInvalidId) { backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - *key_range_, + id(), + object_store_id, + *key_range, indexed_db::CURSOR_NEXT); } else { backing_store_cursor = backing_store_->OpenIndexKeyCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - index_id_, - *key_range_, + id(), + object_store_id, + index_id, + *key_range, indexed_db::CURSOR_NEXT); } if (!backing_store_cursor) { - callbacks_->OnSuccess(count); + callbacks->OnSuccess(count); return; } @@ -1381,7 +1137,7 @@ void CountOperation::Perform(IndexedDBTransaction* transaction) { ++count; } while (backing_store_cursor->Continue()); - callbacks_->OnSuccess(count); + callbacks->OnSuccess(count); } void IndexedDBDatabase::DeleteRange( @@ -1398,27 +1154,34 @@ void IndexedDBDatabase::DeleteRange( if (!ValidateObjectStoreId(object_store_id)) return; - transaction->ScheduleTask(new DeleteRangeOperation( - backing_store_, id(), object_store_id, key_range.Pass(), callbacks)); + transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation, + this, + object_store_id, + base::Passed(&key_range), + callbacks)); } -void DeleteRangeOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("DeleteRangeOperation"); +void IndexedDBDatabase::DeleteRangeOperation( + int64 object_store_id, + scoped_ptr<IndexedDBKeyRange> key_range, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation"); scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor = backing_store_->OpenObjectStoreCursor( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, - *key_range_, + id(), + object_store_id, + *key_range, indexed_db::CURSOR_NEXT); if (backing_store_cursor) { do { if (!backing_store_->DeleteRecord( transaction->BackingStoreTransaction(), - database_id_, - object_store_id_, + id(), + object_store_id, backing_store_cursor->record_identifier())) { - callbacks_->OnError( + callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error deleting data in range")); return; @@ -1426,7 +1189,7 @@ void DeleteRangeOperation::Perform(IndexedDBTransaction* transaction) { } while (backing_store_cursor->Continue()); } - callbacks_->OnSuccess(); + callbacks->OnSuccess(); } void IndexedDBDatabase::Clear(int64 transaction_id, @@ -1441,64 +1204,70 @@ void IndexedDBDatabase::Clear(int64 transaction_id, if (!ValidateObjectStoreId(object_store_id)) return; - transaction->ScheduleTask( - new ClearOperation(backing_store_, id(), object_store_id, callbacks)); + transaction->ScheduleTask(base::Bind( + &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks)); } -void ClearOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("ObjectStoreClearOperation"); - if (!backing_store_->ClearObjectStore(transaction->BackingStoreTransaction(), - database_id_, - object_store_id_)) { - callbacks_->OnError( +void IndexedDBDatabase::ClearOperation( + int64 object_store_id, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation"); + if (!backing_store_->ClearObjectStore( + transaction->BackingStoreTransaction(), id(), object_store_id)) { + callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, "Internal error clearing object store")); return; } - callbacks_->OnSuccess(); + callbacks->OnSuccess(); } -void DeleteObjectStoreOperation::Perform(IndexedDBTransaction* transaction) { - IDB_TRACE("DeleteObjectStoreOperation"); +void IndexedDBDatabase::DeleteObjectStoreOperation( + const IndexedDBObjectStoreMetadata& object_store_metadata, + IndexedDBTransaction* transaction) { + IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation"); bool ok = backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(), transaction->database()->id(), - object_store_metadata_.id); + object_store_metadata.id); if (!ok) { string16 error_string = ASCIIToUTF16("Internal error deleting object store '") + - object_store_metadata_.name + ASCIIToUTF16("'."); + object_store_metadata.name + ASCIIToUTF16("'."); transaction->Abort(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, error_string)); } } -void IndexedDBDatabase::VersionChangeOperation::Perform( +void IndexedDBDatabase::VersionChangeOperation( + int64 version, + scoped_refptr<IndexedDBCallbacks> callbacks, + scoped_ptr<IndexedDBConnection> connection, + WebKit::WebIDBCallbacks::DataLoss data_loss, IndexedDBTransaction* transaction) { - IDB_TRACE("VersionChangeOperation"); - int64 database_id = database_->id(); - int64 old_version = database_->metadata_.int_version; - DCHECK_GT(version_, old_version); - database_->metadata_.int_version = version_; - if (!database_->backing_store_->UpdateIDBDatabaseIntVersion( + IDB_TRACE("IndexedDBDatabase::VersionChangeOperation"); + int64 old_version = metadata_.int_version; + DCHECK_GT(version, old_version); + metadata_.int_version = version; + if (!backing_store_->UpdateIDBDatabaseIntVersion( transaction->BackingStoreTransaction(), - database_id, - database_->metadata_.int_version)) { + id(), + metadata_.int_version)) { IndexedDBDatabaseError error( WebKit::WebIDBDatabaseExceptionUnknownError, ASCIIToUTF16( "Internal error writing data to stable storage when " "updating version.")); - callbacks_->OnError(error); + callbacks->OnError(error); transaction->Abort(error); return; } - DCHECK(!database_->pending_second_half_open_); - - database_->pending_second_half_open_.reset(new PendingSuccessCall( - callbacks_, connection_.get(), transaction_id_, version_)); - callbacks_->OnUpgradeNeeded( - old_version, connection_.Pass(), database_->metadata(), data_loss_); + DCHECK(!pending_second_half_open_); + pending_second_half_open_.reset(new PendingSuccessCall( + callbacks, connection.get(), transaction->id(), version)); + callbacks->OnUpgradeNeeded( + old_version, connection.Pass(), metadata(), data_loss); } void IndexedDBDatabase::TransactionStarted(IndexedDBTransaction* transaction) { @@ -1545,8 +1314,8 @@ void IndexedDBDatabase::TransactionFinishedAndCompleteFired( // Connection was already minted for OnUpgradeNeeded callback. scoped_ptr<IndexedDBConnection> connection; - pending_second_half_open_->Callbacks()->OnSuccess( - connection.Pass(), this->metadata()); + pending_second_half_open_->Callbacks()->OnSuccess(connection.Pass(), + this->metadata()); pending_second_half_open_.reset(); } ProcessPendingCalls(); @@ -1630,6 +1399,9 @@ void IndexedDBDatabase::CreateTransaction( uint16 mode) { DCHECK(connections_.has(connection)); + DCHECK(transactions_.find(transaction_id) == transactions_.end()); + if (transactions_.find(transaction_id) != transactions_.end()) + return; scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction( transaction_id, @@ -1637,7 +1409,6 @@ void IndexedDBDatabase::CreateTransaction( std::set<int64>(object_store_ids.begin(), object_store_ids.end()), static_cast<indexed_db::TransactionMode>(mode), this); - DCHECK(transactions_.find(transaction_id) == transactions_.end()); transactions_[transaction_id] = transaction; } @@ -1765,8 +1536,8 @@ void IndexedDBDatabase::RunVersionChangeTransaction( it != connections_.end(); ++it) { if (*it != connection.get()) { - (*it)->callbacks()->OnVersionChange( - metadata_.int_version, requested_version); + (*it)->callbacks()->OnVersionChange(metadata_.int_version, + requested_version); } } // TODO(jsbell): Remove the call to OnBlocked and instead wait @@ -1816,14 +1587,16 @@ void IndexedDBDatabase::RunVersionChangeTransactionFinal( transactions_[transaction_id]; transaction->ScheduleTask( - new VersionChangeOperation(this, - transaction_id, - requested_version, - callbacks, - connection.Pass(), - data_loss), - new VersionChangeAbortOperation( - this, metadata_.version, metadata_.int_version)); + base::Bind(&IndexedDBDatabase::VersionChangeOperation, + this, + requested_version, + callbacks, + base::Passed(&connection), + data_loss), + base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, + this, + metadata_.version, + metadata_.int_version)); DCHECK(!pending_second_half_open_); } @@ -1925,27 +1698,31 @@ void IndexedDBDatabase::Close(IndexedDBConnection* connection) { } } -void CreateObjectStoreAbortOperation::Perform( +void IndexedDBDatabase::CreateObjectStoreAbortOperation( + int64 object_store_id, IndexedDBTransaction* transaction) { - IDB_TRACE("CreateObjectStoreAbortOperation"); + IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation"); DCHECK(!transaction); - database_->RemoveObjectStore(object_store_id_); + RemoveObjectStore(object_store_id); } -void DeleteObjectStoreAbortOperation::Perform( +void IndexedDBDatabase::DeleteObjectStoreAbortOperation( + const IndexedDBObjectStoreMetadata& object_store_metadata, IndexedDBTransaction* transaction) { - IDB_TRACE("DeleteObjectStoreAbortOperation"); + IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation"); DCHECK(!transaction); - database_->AddObjectStore(object_store_metadata_, - IndexedDBObjectStoreMetadata::kInvalidId); + AddObjectStore(object_store_metadata, + IndexedDBObjectStoreMetadata::kInvalidId); } -void IndexedDBDatabase::VersionChangeAbortOperation::Perform( +void IndexedDBDatabase::VersionChangeAbortOperation( + const string16& previous_version, + int64 previous_int_version, IndexedDBTransaction* transaction) { - IDB_TRACE("VersionChangeAbortOperation"); + IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation"); DCHECK(!transaction); - database_->metadata_.version = previous_version_; - database_->metadata_.int_version = previous_int_version_; + metadata_.version = previous_version; + metadata_.int_version = previous_int_version; } } // namespace content diff --git a/chromium/content/browser/indexed_db/indexed_db_database.h b/chromium/content/browser/indexed_db/indexed_db_database.h index 9c617f5ec63..da7039cc797 100644 --- a/chromium/content/browser/indexed_db/indexed_db_database.h +++ b/chromium/content/browser/indexed_db/indexed_db_database.h @@ -52,7 +52,7 @@ class CONTENT_EXPORT IndexedDBDatabase static scoped_refptr<IndexedDBDatabase> Create( const string16& name, - IndexedDBBackingStore* database, + IndexedDBBackingStore* backing_store, IndexedDBFactory* factory, const Identifier& unique_identifier); scoped_refptr<IndexedDBBackingStore> BackingStore() const; @@ -173,14 +173,72 @@ class CONTENT_EXPORT IndexedDBDatabase // Number of pending deletes, blocked on other connections. size_t PendingDeleteCount() const; + // Asynchronous tasks scheduled within transactions: + void CreateObjectStoreOperation( + const IndexedDBObjectStoreMetadata& object_store_metadata, + IndexedDBTransaction* transaction); + void CreateObjectStoreAbortOperation(int64 object_store_id, + IndexedDBTransaction* transaction); + void DeleteObjectStoreOperation( + const IndexedDBObjectStoreMetadata& object_store_metadata, + IndexedDBTransaction* transaction); + void DeleteObjectStoreAbortOperation( + const IndexedDBObjectStoreMetadata& object_store_metadata, + IndexedDBTransaction* transaction); + void VersionChangeOperation(int64 version, + scoped_refptr<IndexedDBCallbacks> callbacks, + scoped_ptr<IndexedDBConnection> connection, + WebKit::WebIDBCallbacks::DataLoss data_loss, + IndexedDBTransaction* transaction); + void VersionChangeAbortOperation(const string16& previous_version, + int64 previous_int_version, + IndexedDBTransaction* transaction); + void CreateIndexOperation(int64 object_store_id, + const IndexedDBIndexMetadata& index_metadata, + IndexedDBTransaction* transaction); + void DeleteIndexOperation(int64 object_store_id, + const IndexedDBIndexMetadata& index_metadata, + IndexedDBTransaction* transaction); + void CreateIndexAbortOperation(int64 object_store_id, + int64 index_id, + IndexedDBTransaction* transaction); + void DeleteIndexAbortOperation(int64 object_store_id, + const IndexedDBIndexMetadata& index_metadata, + IndexedDBTransaction* transaction); + void GetOperation(int64 object_store_id, + int64 index_id, + scoped_ptr<IndexedDBKeyRange> key_range, + indexed_db::CursorType cursor_type, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction); + struct PutOperationParams; + void PutOperation(scoped_ptr<PutOperationParams> params, + IndexedDBTransaction* transaction); + void SetIndexesReadyOperation(size_t index_count, + IndexedDBTransaction* transaction); + struct OpenCursorOperationParams; + void OpenCursorOperation(scoped_ptr<OpenCursorOperationParams> params, + IndexedDBTransaction* transaction); + void CountOperation(int64 object_store_id, + int64 index_id, + scoped_ptr<IndexedDBKeyRange> key_range, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction); + void DeleteRangeOperation(int64 object_store_id, + scoped_ptr<IndexedDBKeyRange> key_range, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction); + void ClearOperation(int64 object_store_id, + scoped_refptr<IndexedDBCallbacks> callbacks, + IndexedDBTransaction* transaction); + private: friend class base::RefCounted<IndexedDBDatabase>; - IndexedDBDatabase( - const string16& name, - IndexedDBBackingStore* database, - IndexedDBFactory* factory, - const Identifier& unique_identifier); + IndexedDBDatabase(const string16& name, + IndexedDBBackingStore* backing_store, + IndexedDBFactory* factory, + const Identifier& unique_identifier); ~IndexedDBDatabase(); bool IsOpenConnectionBlocked() const; @@ -216,12 +274,6 @@ class CONTENT_EXPORT IndexedDBDatabase bool ValidateObjectStoreIdAndNewIndexId(int64 object_store_id, int64 index_id) const; - class VersionChangeOperation; - - // When a "versionchange" transaction aborts, these restore the back-end - // object hierarchy. - class VersionChangeAbortOperation; - scoped_refptr<IndexedDBBackingStore> backing_store_; IndexedDBDatabaseMetadata metadata_; diff --git a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc index e926a9073d7..1cbc4b82a4a 100644 --- a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc +++ b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.cc @@ -158,6 +158,19 @@ int64 IndexedDBDispatcherHost::RendererTransactionId( return host_transaction_id & 0xffffffff; } +// static +uint32 IndexedDBDispatcherHost::TransactionIdToRendererTransactionId( + int64 host_transaction_id) { + return host_transaction_id & 0xffffffff; +} + +// static +uint32 IndexedDBDispatcherHost::TransactionIdToProcessId( + int64 host_transaction_id) { + return (host_transaction_id >> 32) & 0xffffffff; +} + + IndexedDBCursor* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id) { DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); return cursor_dispatcher_host_->map_.Lookup(ipc_cursor_id); @@ -463,6 +476,12 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction( int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id); + if (transaction_database_map_.find(host_transaction_id) != + transaction_database_map_.end()) { + DLOG(ERROR) << "Duplicate host_transaction_id."; + return; + } + connection->database()->CreateTransaction( host_transaction_id, connection, params.object_store_ids, params.mode); transaction_database_map_[host_transaction_id] = params.ipc_database_id; diff --git a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h index e254cadf486..66db4a7c144 100644 --- a/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h +++ b/chromium/content/browser/indexed_db/indexed_db_dispatcher_host.h @@ -71,9 +71,16 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter { IndexedDBCursor* GetCursorFromId(int32 ipc_cursor_id); + // These are called to map a 32-bit front-end (renderer-specific) transaction + // id to and from a back-end ("host") transaction id that encodes the process + // id in the high 32 bits. The mapping is host-specific and ids are validated. int64 HostTransactionId(int64 transaction_id); int64 RendererTransactionId(int64 host_transaction_id); + // These are called to decode a host transaction ID, for diagnostic purposes. + static uint32 TransactionIdToRendererTransactionId(int64 host_transaction_id); + static uint32 TransactionIdToProcessId(int64 host_transaction_id); + private: // Friends to enable OnDestruct() delegation. friend class BrowserThread; diff --git a/chromium/content/browser/indexed_db/indexed_db_factory.cc b/chromium/content/browser/indexed_db/indexed_db_factory.cc index 6b8d0c80e00..b13b704e1b2 100644 --- a/chromium/content/browser/indexed_db/indexed_db_factory.cc +++ b/chromium/content/browser/indexed_db/indexed_db_factory.cc @@ -36,9 +36,8 @@ IndexedDBFactory::~IndexedDBFactory() {} void IndexedDBFactory::RemoveIDBDatabaseBackend( const IndexedDBDatabase::Identifier& unique_identifier) { - DCHECK(database_backend_map_.find(unique_identifier) != - database_backend_map_.end()); - database_backend_map_.erase(unique_identifier); + DCHECK(database_map_.find(unique_identifier) != database_map_.end()); + database_map_.erase(unique_identifier); } void IndexedDBFactory::GetDatabaseNames( @@ -48,8 +47,9 @@ void IndexedDBFactory::GetDatabaseNames( IDB_TRACE("IndexedDBFactory::GetDatabaseNames"); // TODO(dgrogan): Plumb data_loss back to script eventually? WebKit::WebIDBCallbacks::DataLoss data_loss; - scoped_refptr<IndexedDBBackingStore> backing_store = - OpenBackingStore(origin_identifier, data_directory, &data_loss); + bool disk_full; + scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore( + origin_identifier, data_directory, &data_loss, &disk_full); if (!backing_store) { callbacks->OnError( IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, @@ -68,9 +68,8 @@ void IndexedDBFactory::DeleteDatabase( const base::FilePath& data_directory) { IDB_TRACE("IndexedDBFactory::DeleteDatabase"); IndexedDBDatabase::Identifier unique_identifier(origin_identifier, name); - IndexedDBDatabaseMap::iterator it = - database_backend_map_.find(unique_identifier); - if (it != database_backend_map_.end()) { + IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier); + if (it != database_map_.end()) { // If there are any connections to the database, directly delete the // database. it->second->DeleteDatabase(callbacks); @@ -79,35 +78,39 @@ void IndexedDBFactory::DeleteDatabase( // TODO(dgrogan): Plumb data_loss back to script eventually? WebKit::WebIDBCallbacks::DataLoss data_loss; - scoped_refptr<IndexedDBBackingStore> backing_store = - OpenBackingStore(origin_identifier, data_directory, &data_loss); + bool disk_full = false; + scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore( + origin_identifier, data_directory, &data_loss, &disk_full); if (!backing_store) { - callbacks->OnError(IndexedDBDatabaseError( - WebKit::WebIDBDatabaseExceptionUnknownError, - ASCIIToUTF16("Internal error opening backing store " - "for indexedDB.deleteDatabase."))); + callbacks->OnError( + IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, + ASCIIToUTF16( + "Internal error opening backing store " + "for indexedDB.deleteDatabase."))); return; } - scoped_refptr<IndexedDBDatabase> database_backend = + scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create(name, backing_store, this, unique_identifier); - if (!database_backend) { + if (!database) { callbacks->OnError(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, - ASCIIToUTF16("Internal error creating database backend for " - "indexedDB.deleteDatabase."))); + ASCIIToUTF16( + "Internal error creating database backend for " + "indexedDB.deleteDatabase."))); return; } - database_backend_map_[unique_identifier] = database_backend; - database_backend->DeleteDatabase(callbacks); - database_backend_map_.erase(unique_identifier); + database_map_[unique_identifier] = database; + database->DeleteDatabase(callbacks); + database_map_.erase(unique_identifier); } scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore( const std::string& origin_identifier, const base::FilePath& data_directory, - WebKit::WebIDBCallbacks::DataLoss* data_loss) { + WebKit::WebIDBCallbacks::DataLoss* data_loss, + bool* disk_full) { const std::string file_identifier = ComputeFileIdentifier(origin_identifier); const bool open_in_memory = data_directory.empty(); @@ -120,8 +123,11 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore( if (open_in_memory) { backing_store = IndexedDBBackingStore::OpenInMemory(file_identifier); } else { - backing_store = IndexedDBBackingStore::Open( - origin_identifier, data_directory, file_identifier, data_loss); + backing_store = IndexedDBBackingStore::Open(origin_identifier, + data_directory, + file_identifier, + data_loss, + disk_full); } if (backing_store.get()) { @@ -150,16 +156,24 @@ void IndexedDBFactory::Open( const std::string& origin_identifier, const base::FilePath& data_directory) { IDB_TRACE("IndexedDBFactory::Open"); - scoped_refptr<IndexedDBDatabase> database_backend; + scoped_refptr<IndexedDBDatabase> database; IndexedDBDatabase::Identifier unique_identifier(origin_identifier, name); - IndexedDBDatabaseMap::iterator it = - database_backend_map_.find(unique_identifier); + IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier); WebKit::WebIDBCallbacks::DataLoss data_loss = WebKit::WebIDBCallbacks::DataLossNone; - if (it == database_backend_map_.end()) { - scoped_refptr<IndexedDBBackingStore> backing_store = - OpenBackingStore(origin_identifier, data_directory, &data_loss); + bool disk_full = false; + if (it == database_map_.end()) { + scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore( + origin_identifier, data_directory, &data_loss, &disk_full); if (!backing_store) { + if (disk_full) { + callbacks->OnError( + IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError, + ASCIIToUTF16( + "Encountered full disk while opening " + "backing store for indexedDB.open."))); + return; + } callbacks->OnError(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, ASCIIToUTF16( @@ -167,9 +181,9 @@ void IndexedDBFactory::Open( return; } - database_backend = + database = IndexedDBDatabase::Create(name, backing_store, this, unique_identifier); - if (!database_backend) { + if (!database) { callbacks->OnError(IndexedDBDatabaseError( WebKit::WebIDBDatabaseExceptionUnknownError, ASCIIToUTF16( @@ -177,20 +191,21 @@ void IndexedDBFactory::Open( return; } - database_backend_map_[unique_identifier] = database_backend; + database_map_[unique_identifier] = database; } else { - database_backend = it->second; + database = it->second; } - database_backend->OpenConnection( + database->OpenConnection( callbacks, database_callbacks, transaction_id, version, data_loss); } std::vector<IndexedDBDatabase*> IndexedDBFactory::GetOpenDatabasesForOrigin( const std::string& origin_identifier) const { std::vector<IndexedDBDatabase*> result; - for (IndexedDBDatabaseMap::const_iterator it = database_backend_map_.begin(); - it != database_backend_map_.end(); ++it) { + for (IndexedDBDatabaseMap::const_iterator it = database_map_.begin(); + it != database_map_.end(); + ++it) { if (it->first.first == origin_identifier) result.push_back(it->second.get()); } diff --git a/chromium/content/browser/indexed_db/indexed_db_factory.h b/chromium/content/browser/indexed_db/indexed_db_factory.h index 1666af5e49c..cea2c726f2b 100644 --- a/chromium/content/browser/indexed_db/indexed_db_factory.h +++ b/chromium/content/browser/indexed_db/indexed_db_factory.h @@ -16,7 +16,6 @@ #include "content/browser/indexed_db/indexed_db_callbacks.h" #include "content/browser/indexed_db/indexed_db_database.h" #include "content/browser/indexed_db/indexed_db_database_callbacks.h" -#include "content/browser/indexed_db/indexed_db_factory.h" #include "content/common/content_export.h" namespace content { @@ -57,15 +56,16 @@ class CONTENT_EXPORT IndexedDBFactory virtual ~IndexedDBFactory(); - scoped_refptr<IndexedDBBackingStore> OpenBackingStore( + virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStore( const std::string& origin_identifier, const base::FilePath& data_directory, - WebKit::WebIDBCallbacks::DataLoss* data_loss); + WebKit::WebIDBCallbacks::DataLoss* data_loss, + bool* disk_full); private: typedef std::map<IndexedDBDatabase::Identifier, scoped_refptr<IndexedDBDatabase> > IndexedDBDatabaseMap; - IndexedDBDatabaseMap database_backend_map_; + IndexedDBDatabaseMap database_map_; typedef std::map<std::string, base::WeakPtr<IndexedDBBackingStore> > IndexedDBBackingStoreMap; diff --git a/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc b/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc index a5c90c4117f..230b7887552 100644 --- a/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc +++ b/chromium/content/browser/indexed_db/indexed_db_internals_ui.cc @@ -257,9 +257,6 @@ void IndexedDBInternalsUI::ForceCloseOriginOnIndexedDBThread( void IndexedDBInternalsUI::OnForcedClose(const base::FilePath& partition_path, const GURL& origin_url, size_t connection_count) { - - scoped_refptr<IndexedDBContextImpl> context; - web_ui()->CallJavascriptFunction( "indexeddb.onForcedClose", base::StringValue(partition_path.value()), @@ -281,7 +278,7 @@ void IndexedDBInternalsUI::OnDownloadDataReady( DownloadUrlParameters::FromWebContents(web_ui()->GetWebContents(), url)); DownloadManager* dlm = BrowserContext::GetDownloadManager(browser_context); - const GURL referrer(web_ui()->GetWebContents()->GetURL()); + const GURL referrer(web_ui()->GetWebContents()->GetLastCommittedURL()); dl_params->set_referrer( content::Referrer(referrer, WebKit::WebReferrerPolicyDefault)); diff --git a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc index 0086b82390a..3a7028012a3 100644 --- a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc +++ b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.cc @@ -14,139 +14,137 @@ #include "content/common/indexed_db/indexed_db_key.h" #include "content/common/indexed_db/indexed_db_key_path.h" +// LevelDB Coding Scheme +// ===================== +// // LevelDB stores key/value pairs. Keys and values are strings of bytes, // normally of type std::string. // -// The keys in the backing store are variable-length tuples with different types -// of fields. Each key in the backing store starts with a ternary prefix: -// (database id, object store id, index id). For each, 0 is reserved for -// meta-data. +// The keys in the backing store are variable-length tuples with different +// types of fields. Each key in the backing store starts with a ternary +// prefix: (database id, object store id, index id). For each, 0 is reserved +// for metadata. See KeyPrefix::Decode() for details of the prefix coding. +// // The prefix makes sure that data for a specific database, object store, and -// index are grouped together. The locality is important for performance: common -// operations should only need a minimal number of seek operations. For example, -// all the meta-data for a database is grouped together so that reading that -// meta-data only requires one seek. +// index are grouped together. The locality is important for performance: +// common operations should only need a minimal number of seek operations. For +// example, all the metadata for a database is grouped together so that +// reading that metadata only requires one seek. // // Each key type has a class (in square brackets below) which knows how to // encode, decode, and compare that key type. // -// Global meta-data have keys with prefix (0,0,0), followed by a type byte: -// -// <0, 0, 0, 0> => -// IndexedDB/LevelDB schema version [SchemaVersionKey] -// <0, 0, 0, 1> => The maximum -// database id ever allocated [MaxDatabaseIdKey] -// <0, 0, 0, 2> => -// SerializedScriptValue version [DataVersionKey] -// <0, 0, 0, 100, database id> => Existence -// implies the database id is in the free list [DatabaseFreeListKey] -// <0, 0, 0, 201, utf16 origin name, utf16 database name> => Database id -// [DatabaseNameKey] -// -// -// Database meta-data: +// Strings (origins, names, etc) are encoded as UTF-16BE. // -// Again, the prefix is followed by a type byte. // -// <database id, 0, 0, 0> => utf16 origin name [DatabaseMetaDataKey] -// <database id, 0, 0, 1> => utf16 database name [DatabaseMetaDataKey] -// <database id, 0, 0, 2> => utf16 user version data [DatabaseMetaDataKey] -// <database id, 0, 0, 3> => maximum object store id ever allocated -// [DatabaseMetaDataKey] -// <database id, 0, 0, 4> => user integer version (var int) -// [DatabaseMetaDataKey] +// Global metadata +// --------------- +// The prefix is <0, 0, 0>, followed by a metadata type byte: // +// <0, 0, 0, 0> => backing store schema version [SchemaVersionKey] +// <0, 0, 0, 1> => maximum allocated database [MaxDatabaseIdKey] +// <0, 0, 0, 2> => SerializedScriptValue version [DataVersionKey] +// <0, 0, 0, 100, database id> +// => Existence implies the database id is in the free list +// [DatabaseFreeListKey] +// <0, 0, 0, 201, origin, database name> => Database id [DatabaseNameKey] // -// Object store meta-data: // -// The prefix is followed by a type byte, then a variable-length integer, -// and then another type byte. +// Database metadata: [DatabaseMetaDataKey] +// ---------------------------------------- +// The prefix is <database id, 0, 0> followed by a metadata type byte: // -// <database id, 0, 0, 50, object store id, 0> => utf16 object store name -// [ObjectStoreMetaDataKey] -// <database id, 0, 0, 50, object store id, 1> => utf16 key path -// [ObjectStoreMetaDataKey] -// <database id, 0, 0, 50, object store id, 2> => has auto increment -// [ObjectStoreMetaDataKey] -// <database id, 0, 0, 50, object store id, 3> => is evictable -// [ObjectStoreMetaDataKey] -// <database id, 0, 0, 50, object store id, 4> => last "version" number -// [ObjectStoreMetaDataKey] -// <database id, 0, 0, 50, object store id, 5> => maximum index id ever -// allocated [ObjectStoreMetaDataKey] -// <database id, 0, 0, 50, object store id, 6> => has key path (vs. null) -// [ObjectStoreMetaDataKey] -// <database id, 0, 0, 50, object store id, 7> => key generator current -// number [ObjectStoreMetaDataKey] +// <database id, 0, 0, 0> => origin name +// <database id, 0, 0, 1> => database name +// <database id, 0, 0, 2> => IDB string version data (obsolete) +// <database id, 0, 0, 3> => maximum allocated object store id +// <database id, 0, 0, 4> => IDB integer version (var int) // // -// Index meta-data: +// Object store metadata: [ObjectStoreMetaDataKey] +// ----------------------------------------------- +// The prefix is <database id, 0, 0>, followed by a type byte (50), then the +// object store id (var int), then a metadata type byte. // -// The prefix is followed by a type byte, then two variable-length integers, -// and then another type byte. +// <database id, 0, 0, 50, object store id, 0> => object store name +// <database id, 0, 0, 50, object store id, 1> => key path +// <database id, 0, 0, 50, object store id, 2> => auto increment flag +// <database id, 0, 0, 50, object store id, 3> => is evictable +// <database id, 0, 0, 50, object store id, 4> => last "version" number +// <database id, 0, 0, 50, object store id, 5> => maximum allocated index id +// <database id, 0, 0, 50, object store id, 6> => has key path flag (obsolete) +// <database id, 0, 0, 50, object store id, 7> => key generator current number // -// <database id, 0, 0, 100, object store id, index id, 0> => utf16 index -// name [IndexMetaDataKey] -// <database id, 0, 0, 100, object store id, index id, 1> => are index keys -// unique [IndexMetaDataKey] -// <database id, 0, 0, 100, object store id, index id, 2> => utf16 key path -// [IndexMetaDataKey] -// <database id, 0, 0, 100, object store id, index id, 3> => is index -// multi-entry [IndexMetaDataKey] +// The key path was originally just a string (#1) or null (identified by flag, +// #6). To support null, string, or array the coding is now identified by the +// leading bytes in #1 - see EncodeIDBKeyPath. // +// The "version" field is used to weed out stale index data. Whenever new +// object store data is inserted, it gets a new "version" number, and new +// index data is written with this number. When the index is used for +// look-ups, entries are validated against the "exists" entries, and records +// with old "version" numbers are deleted when they are encountered in +// GetPrimaryKeyViaIndex, IndexCursorImpl::LoadCurrentRow and +// IndexKeyCursorImpl::LoadCurrentRow. // -// Other object store and index meta-data: // -// The prefix is followed by a type byte. The object store and index id are -// variable length integers, the utf16 strings are variable length strings. +// Index metadata: [IndexMetaDataKey] +// ---------------------------------- +// The prefix is <database id, 0, 0>, followed by a type byte (100), then the +// object store id (var int), then the index id (var int), then a metadata +// type byte. // -// <database id, 0, 0, 150, object store id> => existence -// implies the object store id is in the free list [ObjectStoreFreeListKey] -// <database id, 0, 0, 151, object store id, index id> => existence -// implies the index id is in the free list [IndexFreeListKey] -// <database id, 0, 0, 200, utf16 object store name> => object -// store id [ObjectStoreNamesKey] -// <database id, 0, 0, 201, object store id, utf16 index name> => index id -// [IndexNamesKey] +// <database id, 0, 0, 100, object store id, index id, 0> => index name +// <database id, 0, 0, 100, object store id, index id, 1> => unique flag +// <database id, 0, 0, 100, object store id, index id, 2> => key path +// <database id, 0, 0, 100, object store id, index id, 3> => multi-entry flag // // -// Object store data: +// Other object store and index metadata +// ------------------------------------- +// The prefix is <database id, 0, 0> followed by a type byte. The object +// store and index id are variable length integers, the names are variable +// length strings. // -// The prefix is followed by a type byte. The user key is an encoded -// IndexedDBKey. +// <database id, 0, 0, 150, object store id> +// => existence implies the object store id is in the free list +// [ObjectStoreFreeListKey] +// <database id, 0, 0, 151, object store id, index id> +// => existence implies the index id is in the free list [IndexFreeListKey] +// <database id, 0, 0, 200, object store name> +// => object store id [ObjectStoreNamesKey] +// <database id, 0, 0, 201, object store id, index name> +// => index id [IndexNamesKey] // -// <database id, object store id, 1, user key> => "version", serialized -// script value [ObjectStoreDataKey] // +// Object store data: [ObjectStoreDataKey] +// --------------------------------------- +// The prefix is followed by a type byte and the encoded IDB primary key. The +// data has a "version" prefix followed by the serialized script value. // -// "Exists" entry: +// <database id, object store id, 1, user key> +// => "version", serialized script value // -// The prefix is followed by a type byte. The user key is an encoded -// IndexedDBKey. // -// <database id, object store id, 2, user key> => "version" [ExistsEntryKey] +// "Exists" entry: [ExistsEntryKey] +// -------------------------------- +// The prefix is followed by a type byte and the encoded IDB primary key. // +// <database id, object store id, 2, user key> => "version" // -// Index data: // -// The prefix is followed by a type byte. The index key is an encoded -// IndexedDBKey. The sequence number is a variable length integer. -// The primary key is an encoded IndexedDBKey. +// Index data +// ---------- +// The prefix is followed by a type byte, the encoded IDB index key, a +// "sequence" number (obsolete; var int), and the encoded IDB primary key. // -// <database id, object store id, index id, index key, sequence number, -// primary key> => "version", primary key [IndexDataKey] +// <database id, object store id, index id, index key, sequence number, +// primary key> => "version", primary key [IndexDataKey] // -// (The sequence number is obsolete; it was used to allow two entries with -// the same user (index) key in non-unique indexes prior to the inclusion of -// the primary key in the data. The "version" field is used to weed out -// stale -// index data. Whenever new object store data is inserted, it gets a new -// "version" number, and new index data is written with this number. When -// the index is used for look-ups, entries are validated against the -// "exists" entries, and records with old "version" numbers are deleted -// when they are encountered in get_primary_key_via_index, -// IndexCursorImpl::load_current_row, and -// IndexKeyCursorImpl::load_current_row). +// The sequence number is obsolete; it was used to allow two entries with the +// same user (index) key in non-unique indexes prior to the inclusion of the +// primary key in the data. + using base::StringPiece; using WebKit::WebIDBKeyType; @@ -675,11 +673,21 @@ static int CompareInts(int64 a, int64 b) { return 0; } +static inline int CompareSizes(size_t a, size_t b) { + if (a > b) + return 1; + if (b > a) + return -1; + return 0; +} + static int CompareTypes(WebIDBKeyType a, WebIDBKeyType b) { return b - a; } int CompareEncodedIDBKeys(StringPiece* slice_a, StringPiece* slice_b, bool* ok) { + DCHECK(!slice_a->empty()); + DCHECK(!slice_b->empty()); *ok = true; unsigned char type_a = (*slice_a)[0]; unsigned char type_b = (*slice_b)[0]; @@ -744,7 +752,10 @@ int CompareEncodedIDBKeys(const std::string& key_a, namespace { template <typename KeyType> -int Compare(const StringPiece& a, const StringPiece& b, bool, bool* ok) { +int Compare(const StringPiece& a, + const StringPiece& b, + bool only_compare_index_keys, + bool* ok) { KeyType key_a; KeyType key_b; @@ -763,10 +774,29 @@ int Compare(const StringPiece& a, const StringPiece& b, bool, bool* ok) { return key_a.Compare(key_b); } +template <typename KeyType> +int CompareSuffix(StringPiece* a, + StringPiece* b, + bool only_compare_index_keys, + bool* ok) { + NOTREACHED(); + return 0; +} + +template <> +int CompareSuffix<ExistsEntryKey>(StringPiece* slice_a, + StringPiece* slice_b, + bool only_compare_index_keys, + bool* ok) { + DCHECK(!slice_a->empty()); + DCHECK(!slice_b->empty()); + return CompareEncodedIDBKeys(slice_a, slice_b, ok); +} + template <> int Compare<ExistsEntryKey>(const StringPiece& a, const StringPiece& b, - bool, + bool only_compare_index_keys, bool* ok) { KeyPrefix prefix_a; KeyPrefix prefix_b; @@ -787,13 +817,22 @@ int Compare<ExistsEntryKey>(const StringPiece& a, // Prefixes are not compared - it is assumed this was already done. DCHECK(!prefix_a.Compare(prefix_b)); - return CompareEncodedIDBKeys(&slice_a, &slice_b, ok); + return CompareSuffix<ExistsEntryKey>( + &slice_a, &slice_b, only_compare_index_keys, ok); +} + +template <> +int CompareSuffix<ObjectStoreDataKey>(StringPiece* slice_a, + StringPiece* slice_b, + bool only_compare_index_keys, + bool* ok) { + return CompareEncodedIDBKeys(slice_a, slice_b, ok); } template <> int Compare<ObjectStoreDataKey>(const StringPiece& a, const StringPiece& b, - bool, + bool only_compare_index_keys, bool* ok) { KeyPrefix prefix_a; KeyPrefix prefix_b; @@ -814,13 +853,45 @@ int Compare<ObjectStoreDataKey>(const StringPiece& a, // Prefixes are not compared - it is assumed this was already done. DCHECK(!prefix_a.Compare(prefix_b)); - return CompareEncodedIDBKeys(&slice_a, &slice_b, ok); + return CompareSuffix<ObjectStoreDataKey>( + &slice_a, &slice_b, only_compare_index_keys, ok); +} + +template <> +int CompareSuffix<IndexDataKey>(StringPiece* slice_a, + StringPiece* slice_b, + bool only_compare_index_keys, + bool* ok) { + // index key + int result = CompareEncodedIDBKeys(slice_a, slice_b, ok); + if (!*ok || result) + return result; + if (only_compare_index_keys) + return 0; + + // sequence number [optional] + int64 sequence_number_a = -1; + int64 sequence_number_b = -1; + if (!slice_a->empty() && !DecodeVarInt(slice_a, &sequence_number_a)) + return 0; + if (!slice_b->empty() && !DecodeVarInt(slice_b, &sequence_number_b)) + return 0; + + if (slice_a->empty() || slice_b->empty()) + return CompareSizes(slice_a->size(), slice_b->size()); + + // primary key [optional] + result = CompareEncodedIDBKeys(slice_a, slice_b, ok); + if (!*ok || result) + return result; + + return CompareInts(sequence_number_a, sequence_number_b); } template <> int Compare<IndexDataKey>(const StringPiece& a, const StringPiece& b, - bool ignore_duplicates, + bool only_compare_index_keys, bool* ok) { KeyPrefix prefix_a; KeyPrefix prefix_b; @@ -841,43 +912,13 @@ int Compare<IndexDataKey>(const StringPiece& a, // Prefixes are not compared - it is assumed this was already done. DCHECK(!prefix_a.Compare(prefix_b)); - // index key - int result = CompareEncodedIDBKeys(&slice_a, &slice_b, ok); - if (!*ok || result) - return result; - if (ignore_duplicates) - return 0; - - // sequence number [optional] - int64 sequence_number_a = -1; - int64 sequence_number_b = -1; - if (!slice_a.empty()) { - if (!DecodeVarInt(&slice_a, &sequence_number_a)) - return 0; - } - if (!slice_b.empty()) { - if (!DecodeVarInt(&slice_b, &sequence_number_b)) - return 0; - } - - // primary key [optional] - if (slice_a.empty() && slice_b.empty()) - return 0; - if (slice_a.empty()) - return -1; - if (slice_b.empty()) - return 1; - - result = CompareEncodedIDBKeys(&slice_a, &slice_b, ok); - if (!*ok || result) - return result; - - return CompareInts(sequence_number_a, sequence_number_b); + return CompareSuffix<IndexDataKey>( + &slice_a, &slice_b, only_compare_index_keys, ok); } int Compare(const StringPiece& a, const StringPiece& b, - bool index_keys, + bool only_compare_index_keys, bool* ok) { StringPiece slice_a(a); StringPiece slice_b(b); @@ -918,11 +959,16 @@ int Compare(const StringPiece& a, if (type_byte_a < kMaxSimpleGlobalMetaDataTypeByte) return 0; - const bool ignore_duplicates = false; + // Compare<> is used (which re-decodes the prefix) rather than an + // specialized CompareSuffix<> because metadata is relatively uncommon + // in the database. + if (type_byte_a == kDatabaseFreeListTypeByte) - return Compare<DatabaseFreeListKey>(a, b, ignore_duplicates, ok); + return Compare<DatabaseFreeListKey>( + a, b, only_compare_index_keys, ok); if (type_byte_a == kDatabaseNameTypeByte) - return Compare<DatabaseNameKey>(a, b, ignore_duplicates, ok); + return Compare<DatabaseNameKey>( + a, b, /*only_compare_index_keys*/ false, ok); break; } @@ -947,50 +993,56 @@ int Compare(const StringPiece& a, if (type_byte_a < DatabaseMetaDataKey::MAX_SIMPLE_METADATA_TYPE) return 0; - const bool ignore_duplicates = false; + // Compare<> is used (which re-decodes the prefix) rather than an + // specialized CompareSuffix<> because metadata is relatively uncommon + // in the database. + if (type_byte_a == kObjectStoreMetaDataTypeByte) - return Compare<ObjectStoreMetaDataKey>(a, b, ignore_duplicates, ok); + return Compare<ObjectStoreMetaDataKey>( + a, b, only_compare_index_keys, ok); if (type_byte_a == kIndexMetaDataTypeByte) - return Compare<IndexMetaDataKey>(a, b, ignore_duplicates, ok); + return Compare<IndexMetaDataKey>( + a, b, /*only_compare_index_keys*/ false, ok); if (type_byte_a == kObjectStoreFreeListTypeByte) - return Compare<ObjectStoreFreeListKey>(a, b, ignore_duplicates, ok); + return Compare<ObjectStoreFreeListKey>( + a, b, only_compare_index_keys, ok); if (type_byte_a == kIndexFreeListTypeByte) - return Compare<IndexFreeListKey>(a, b, ignore_duplicates, ok); + return Compare<IndexFreeListKey>( + a, b, /*only_compare_index_keys*/ false, ok); if (type_byte_a == kObjectStoreNamesTypeByte) - return Compare<ObjectStoreNamesKey>(a, b, ignore_duplicates, ok); + return Compare<ObjectStoreNamesKey>( + a, b, only_compare_index_keys, ok); if (type_byte_a == kIndexNamesKeyTypeByte) - return Compare<IndexNamesKey>(a, b, ignore_duplicates, ok); + return Compare<IndexNamesKey>( + a, b, /*only_compare_index_keys*/ false, ok); break; } case KeyPrefix::OBJECT_STORE_DATA: { + // Provide a stable ordering for invalid data. if (slice_a.empty() || slice_b.empty()) - return slice_a.size() - slice_b.size(); - // TODO(jsbell): This case of non-existing user keys should not have to be - // handled this way. + return CompareSizes(slice_a.size(), slice_b.size()); - const bool ignore_duplicates = false; - return Compare<ObjectStoreDataKey>(a, b, ignore_duplicates, ok); + return CompareSuffix<ObjectStoreDataKey>( + &slice_a, &slice_b, /*only_compare_index_keys*/ false, ok); } case KeyPrefix::EXISTS_ENTRY: { + // Provide a stable ordering for invalid data. if (slice_a.empty() || slice_b.empty()) - return slice_a.size() - slice_b.size(); - // TODO(jsbell): This case of non-existing user keys should not have to be - // handled this way. + return CompareSizes(slice_a.size(), slice_b.size()); - const bool ignore_duplicates = false; - return Compare<ExistsEntryKey>(a, b, ignore_duplicates, ok); + return CompareSuffix<ExistsEntryKey>( + &slice_a, &slice_b, /*only_compare_index_keys*/ false, ok); } case KeyPrefix::INDEX_DATA: { + // Provide a stable ordering for invalid data. if (slice_a.empty() || slice_b.empty()) - return slice_a.size() - slice_b.size(); - // TODO(jsbell): This case of non-existing user keys should not have to be - // handled this way. + return CompareSizes(slice_a.size(), slice_b.size()); - bool ignore_duplicates = index_keys; - return Compare<IndexDataKey>(a, b, ignore_duplicates, ok); + return CompareSuffix<IndexDataKey>( + &slice_a, &slice_b, only_compare_index_keys, ok); } case KeyPrefix::INVALID_TYPE: @@ -1004,9 +1056,11 @@ int Compare(const StringPiece& a, } // namespace -int Compare(const StringPiece& a, const StringPiece& b, bool index_keys) { +int Compare(const StringPiece& a, + const StringPiece& b, + bool only_compare_index_keys) { bool ok; - int result = Compare(a, b, index_keys, &ok); + int result = Compare(a, b, only_compare_index_keys, &ok); DCHECK(ok); if (!ok) return 0; @@ -1357,10 +1411,7 @@ int ObjectStoreMetaDataKey::Compare(const ObjectStoreMetaDataKey& other) { DCHECK_GE(object_store_id_, 0); if (int x = CompareInts(object_store_id_, other.object_store_id_)) return x; - int64 result = meta_data_type_ - other.meta_data_type_; - if (result < 0) - return -1; - return (result > 0) ? 1 : result; + return meta_data_type_ - other.meta_data_type_; } IndexMetaDataKey::IndexMetaDataKey() @@ -1782,7 +1833,7 @@ std::string IndexDataKey::EncodeMaxKey(int64 database_id, } int IndexDataKey::Compare(const IndexDataKey& other, - bool ignore_duplicates, + bool only_compare_index_keys, bool* ok) { DCHECK_GE(database_id_, 0); DCHECK_GE(object_store_id_, 0); @@ -1791,7 +1842,7 @@ int IndexDataKey::Compare(const IndexDataKey& other, CompareEncodedIDBKeys(encoded_user_key_, other.encoded_user_key_, ok); if (!*ok || result) return result; - if (ignore_duplicates) + if (only_compare_index_keys) return 0; result = CompareEncodedIDBKeys( encoded_primary_key_, other.encoded_primary_key_, ok); diff --git a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h index ecd8b9b2d99..974c27a4fa2 100644 --- a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h +++ b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding.h @@ -402,7 +402,9 @@ class IndexDataKey { CONTENT_EXPORT static std::string EncodeMaxKey(int64 database_id, int64 object_store_id, int64 index_id); - int Compare(const IndexDataKey& other, bool ignore_duplicates, bool* ok); + int Compare(const IndexDataKey& other, + bool only_compare_index_keys, + bool* ok); int64 DatabaseId() const; int64 ObjectStoreId() const; int64 IndexId() const; diff --git a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc index c9f39762438..01f1260828b 100644 --- a/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc +++ b/chromium/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc @@ -799,10 +799,13 @@ TEST(IndexedDBLevelDBCodingTest, ComparisonTest) { keys.push_back(IndexNamesKey::Encode(1, 1, ASCIIToUTF16(""))); keys.push_back(IndexNamesKey::Encode(1, 1, ASCIIToUTF16("a"))); keys.push_back(IndexNamesKey::Encode(1, 2, ASCIIToUTF16("a"))); + keys.push_back(ObjectStoreDataKey::Encode(1, 1, std::string())); keys.push_back(ObjectStoreDataKey::Encode(1, 1, MinIDBKey())); keys.push_back(ObjectStoreDataKey::Encode(1, 1, MaxIDBKey())); + keys.push_back(ExistsEntryKey::Encode(1, 1, std::string())); keys.push_back(ExistsEntryKey::Encode(1, 1, MinIDBKey())); keys.push_back(ExistsEntryKey::Encode(1, 1, MaxIDBKey())); + keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), std::string(), 0)); keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0)); keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1)); keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0)); diff --git a/chromium/content/browser/indexed_db/indexed_db_quota_client.cc b/chromium/content/browser/indexed_db/indexed_db_quota_client.cc index 1b187c51339..5e0cea898e0 100644 --- a/chromium/content/browser/indexed_db/indexed_db_quota_client.cc +++ b/chromium/content/browser/indexed_db/indexed_db_quota_client.cc @@ -175,4 +175,8 @@ void IndexedDBQuotaClient::DeleteOriginData(const GURL& origin, callback); } +bool IndexedDBQuotaClient::DoesSupport(quota::StorageType type) const { + return type == quota::kStorageTypeTemporary; +} + } // namespace content diff --git a/chromium/content/browser/indexed_db/indexed_db_quota_client.h b/chromium/content/browser/indexed_db/indexed_db_quota_client.h index 6c0f2bb2af7..3c48c279ccf 100644 --- a/chromium/content/browser/indexed_db/indexed_db_quota_client.h +++ b/chromium/content/browser/indexed_db/indexed_db_quota_client.h @@ -43,6 +43,7 @@ class IndexedDBQuotaClient : public quota::QuotaClient, virtual void DeleteOriginData(const GURL& origin, quota::StorageType type, const DeletionCallback& callback) OVERRIDE; + virtual bool DoesSupport(quota::StorageType type) const OVERRIDE; private: scoped_refptr<IndexedDBContextImpl> indexed_db_context_; diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction.cc b/chromium/content/browser/indexed_db/indexed_db_transaction.cc index b096f3b3319..3916726f531 100644 --- a/chromium/content/browser/indexed_db/indexed_db_transaction.cc +++ b/chromium/content/browser/indexed_db/indexed_db_transaction.cc @@ -23,15 +23,14 @@ IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); } void IndexedDBTransaction::TaskQueue::clear() { while (!queue_.empty()) - scoped_ptr<Operation> task(pop()); + queue_.pop(); } -scoped_ptr<IndexedDBTransaction::Operation> -IndexedDBTransaction::TaskQueue::pop() { +IndexedDBTransaction::Operation IndexedDBTransaction::TaskQueue::pop() { DCHECK(!queue_.empty()); - scoped_ptr<Operation> task(queue_.front()); + Operation task(queue_.front()); queue_.pop(); - return task.Pass(); + return task; } IndexedDBTransaction::TaskStack::TaskStack() {} @@ -39,15 +38,14 @@ IndexedDBTransaction::TaskStack::~TaskStack() { clear(); } void IndexedDBTransaction::TaskStack::clear() { while (!stack_.empty()) - scoped_ptr<Operation> task(pop()); + stack_.pop(); } -scoped_ptr<IndexedDBTransaction::Operation> -IndexedDBTransaction::TaskStack::pop() { +IndexedDBTransaction::Operation IndexedDBTransaction::TaskStack::pop() { DCHECK(!stack_.empty()); - scoped_ptr<Operation> task(stack_.top()); + Operation task(stack_.top()); stack_.pop(); - return task.Pass(); + return task; } IndexedDBTransaction::IndexedDBTransaction( @@ -65,7 +63,11 @@ IndexedDBTransaction::IndexedDBTransaction( database_(database), transaction_(database->BackingStore().get()), should_process_queue_(false), - pending_preemptive_events_(0) { + pending_preemptive_events_(0), + queue_status_(CREATED), + creation_time_(base::Time::Now()), + tasks_scheduled_(0), + tasks_completed_(0) { database_->transaction_coordinator().DidCreateTransaction(this); } @@ -78,20 +80,30 @@ IndexedDBTransaction::~IndexedDBTransaction() { DCHECK(abort_task_stack_.empty()); } +void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) { + if (state_ == FINISHED) + return; + task_queue_.push(task); + ++tasks_scheduled_; + abort_task_stack_.push(abort_task); + EnsureTasksRunning(); +} + void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type, - Operation* task, - Operation* abort_task) { + Operation task) { if (state_ == FINISHED) return; - if (type == IndexedDBDatabase::NORMAL_TASK) + if (type == IndexedDBDatabase::NORMAL_TASK) { task_queue_.push(task); - else + ++tasks_scheduled_; + } else { preemptive_task_queue_.push(task); + } + EnsureTasksRunning(); +} - if (abort_task) - abort_task_stack_.push(abort_task); - +void IndexedDBTransaction::EnsureTasksRunning() { if (state_ == UNUSED) { Start(); } else if (state_ == RUNNING && !should_process_queue_) { @@ -126,8 +138,8 @@ void IndexedDBTransaction::Abort(const IndexedDBDatabaseError& error) { // Run the abort tasks, if any. while (!abort_task_stack_.empty()) { - scoped_ptr<Operation> task(abort_task_stack_.pop()); - task->Perform(0); + Operation task(abort_task_stack_.pop()); + task.Run(0); } preemptive_task_queue_.clear(); task_queue_.clear(); @@ -177,6 +189,7 @@ void IndexedDBTransaction::Run() { DCHECK(state_ == START_PENDING || state_ == RUNNING); DCHECK(!should_process_queue_); + start_time_ = base::Time::Now(); should_process_queue_ = true; base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&IndexedDBTransaction::ProcessTaskQueue, this)); @@ -272,8 +285,12 @@ void IndexedDBTransaction::ProcessTaskQueue() { pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_; while (!task_queue->empty() && state_ != FINISHED) { DCHECK_EQ(state_, RUNNING); - scoped_ptr<Operation> task(task_queue->pop()); - task->Perform(this); + Operation task(task_queue->pop()); + task.Run(this); + if (!pending_preemptive_events_) { + DCHECK(tasks_completed_ < tasks_scheduled_); + ++tasks_completed_; + } // Event itself may change which queue should be processed next. task_queue = diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction.h b/chromium/content/browser/indexed_db/indexed_db_transaction.h index 51b1c0ad2fb..24f44490537 100644 --- a/chromium/content/browser/indexed_db/indexed_db_transaction.h +++ b/chromium/content/browser/indexed_db/indexed_db_transaction.h @@ -12,6 +12,7 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/time/time.h" #include "content/browser/indexed_db/indexed_db_backing_store.h" #include "content/browser/indexed_db/indexed_db_database.h" #include "content/browser/indexed_db/indexed_db_database_error.h" @@ -23,6 +24,8 @@ class IndexedDBDatabaseCallbacks; class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> { public: + typedef base::Callback<void(IndexedDBTransaction*)> Operation; + IndexedDBTransaction(int64 id, scoped_refptr<IndexedDBDatabaseCallbacks> callbacks, const std::set<int64>& object_store_ids, @@ -32,29 +35,16 @@ class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> { virtual void Abort(); void Commit(); - class Operation { - public: - Operation() {} - virtual ~Operation() {} - virtual void Perform(IndexedDBTransaction* transaction) = 0; - }; - void Abort(const IndexedDBDatabaseError& error); void Run(); indexed_db::TransactionMode mode() const { return mode_; } const std::set<int64>& scope() const { return object_store_ids_; } - void ScheduleTask(Operation* task) { - ScheduleTask(IndexedDBDatabase::NORMAL_TASK, task, NULL); - } - void ScheduleTask(Operation* task, Operation* abort_task) { - ScheduleTask(IndexedDBDatabase::NORMAL_TASK, task, abort_task); - } - void ScheduleTask(IndexedDBDatabase::TaskType task_type, Operation* task) { - ScheduleTask(task_type, task, NULL); + void ScheduleTask(Operation task) { + ScheduleTask(IndexedDBDatabase::NORMAL_TASK, task); } - void ScheduleTask(IndexedDBDatabase::TaskType task_type, - Operation* task, - Operation* abort_task); + + void ScheduleTask(Operation task, Operation abort_task); + void ScheduleTask(IndexedDBDatabase::TaskType, Operation task); void RegisterOpenCursor(IndexedDBCursor* cursor); void UnregisterOpenCursor(IndexedDBCursor* cursor); void AddPreemptiveEvent() { pending_preemptive_events_++; } @@ -71,6 +61,20 @@ class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> { IndexedDBDatabaseCallbacks* connection() const { return callbacks_; } bool IsRunning() const { return state_ == RUNNING; } + // The following types/accessors are for diagnostics only. + enum QueueStatus { + CREATED, + BLOCKED, + UNBLOCKED, + }; + + QueueStatus queue_status() const { return queue_status_; } + void set_queue_status(QueueStatus status) { queue_status_ = status; } + base::Time creation_time() const { return creation_time_; } + base::Time start_time() const { return start_time_; } + int tasks_scheduled() const { return tasks_scheduled_; } + int tasks_completed() const { return tasks_completed_; } + protected: virtual ~IndexedDBTransaction(); friend class base::RefCounted<IndexedDBTransaction>; @@ -84,6 +88,7 @@ class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> { FINISHED, // Either aborted or committed. }; + void EnsureTasksRunning(); void Start(); bool IsTaskQueueEmpty() const; @@ -106,12 +111,12 @@ class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> { TaskQueue(); ~TaskQueue(); bool empty() const { return queue_.empty(); } - void push(Operation* task) { queue_.push(task); } - scoped_ptr<Operation> pop(); + void push(Operation task) { queue_.push(task); } + Operation pop(); void clear(); private: - std::queue<Operation*> queue_; + std::queue<Operation> queue_; }; class TaskStack { @@ -119,12 +124,12 @@ class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> { TaskStack(); ~TaskStack(); bool empty() const { return stack_.empty(); } - void push(Operation* task) { stack_.push(task); } - scoped_ptr<Operation> pop(); + void push(Operation task) { stack_.push(task); } + Operation pop(); void clear(); private: - std::stack<Operation*> stack_; + std::stack<Operation> stack_; }; TaskQueue task_queue_; @@ -137,6 +142,13 @@ class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> { int pending_preemptive_events_; std::set<IndexedDBCursor*> open_cursors_; + + // The following members are for diagnostics only. + QueueStatus queue_status_; + base::Time creation_time_; + base::Time start_time_; + int tasks_scheduled_; + int tasks_completed_; }; } // namespace content diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc index 1f5100d26ac..7be7bcafcaf 100644 --- a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc +++ b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.cc @@ -21,6 +21,7 @@ IndexedDBTransactionCoordinator::~IndexedDBTransactionCoordinator() { void IndexedDBTransactionCoordinator::DidCreateTransaction( IndexedDBTransaction* transaction) { DCHECK(transactions_.find(transaction) == transactions_.end()); + DCHECK(transaction->queue_status() == IndexedDBTransaction::CREATED); transactions_[transaction] = transaction; } @@ -92,62 +93,81 @@ void IndexedDBTransactionCoordinator::ProcessStartedTransactions() { (*started_transactions_.begin())->mode() != indexed_db::TRANSACTION_VERSION_CHANGE); + // The locked_scope set accumulates the ids of object stores in the scope of + // running read/write transactions. Other read-write transactions with + // stores in this set may not be started. Read-only transactions may start, + // taking a snapshot of the database, which does not include uncommitted + // data. ("Version change" transactions are exclusive, but handled by the + // connection sequencing in IndexedDBDatabase.) + std::set<int64> locked_scope; + for (list_set<IndexedDBTransaction*>::const_iterator it = + started_transactions_.begin(); + it != started_transactions_.end(); + ++it) { + IndexedDBTransaction* transaction = *it; + if (transaction->mode() == indexed_db::TRANSACTION_READ_WRITE) { + // Running read/write transactions have exclusive access to the object + // stores within their scopes. + locked_scope.insert(transaction->scope().begin(), + transaction->scope().end()); + } + } + list_set<IndexedDBTransaction*>::const_iterator it = queued_transactions_.begin(); while (it != queued_transactions_.end()) { IndexedDBTransaction* transaction = *it; ++it; - if (CanRunTransaction(transaction)) { + if (CanRunTransaction(transaction, locked_scope)) { + transaction->set_queue_status(IndexedDBTransaction::UNBLOCKED); queued_transactions_.erase(transaction); started_transactions_.insert(transaction); transaction->Run(); + } else { + transaction->set_queue_status(IndexedDBTransaction::BLOCKED); + } + if (transaction->mode() == indexed_db::TRANSACTION_READ_WRITE) { + // Either the transaction started, so it has exclusive access to the + // stores in its scope, or per the spec the transaction which was + // created first must get access first, so the stores are also locked. + locked_scope.insert(transaction->scope().begin(), + transaction->scope().end()); } } } -static bool DoScopesOverlap(const std::set<int64>& scope1, - const std::set<int64>& scope2) { - for (std::set<int64>::const_iterator it = scope1.begin(); it != scope1.end(); - ++it) { - if (scope2.find(*it) != scope2.end()) +template<typename T> +static bool DoSetsIntersect(const std::set<T>& set1, + const std::set<T>& set2) { + typename std::set<T>::const_iterator it1 = set1.begin(); + typename std::set<T>::const_iterator it2 = set2.begin(); + while (it1 != set1.end() && it2 != set2.end()) { + if (*it1 < *it2) + ++it1; + else if (*it2 < *it1) + ++it2; + else return true; } return false; } bool IndexedDBTransactionCoordinator::CanRunTransaction( - IndexedDBTransaction* transaction) { + IndexedDBTransaction* const transaction, + const std::set<int64>& locked_scope) const { DCHECK(queued_transactions_.has(transaction)); switch (transaction->mode()) { case indexed_db::TRANSACTION_VERSION_CHANGE: DCHECK_EQ(static_cast<size_t>(1), queued_transactions_.size()); DCHECK(started_transactions_.empty()); + DCHECK(locked_scope.empty()); return true; case indexed_db::TRANSACTION_READ_ONLY: return true; case indexed_db::TRANSACTION_READ_WRITE: - for (list_set<IndexedDBTransaction*>::const_iterator it = - started_transactions_.begin(); - it != started_transactions_.end(); - ++it) { - IndexedDBTransaction* other = *it; - if (other->mode() == indexed_db::TRANSACTION_READ_WRITE && - DoScopesOverlap(transaction->scope(), other->scope())) - return false; - } - for (list_set<IndexedDBTransaction*>::const_iterator it = - queued_transactions_.begin(); - *it != transaction; - ++it) { - DCHECK(it != queued_transactions_.end()); - IndexedDBTransaction* other = *it; - if (other->mode() == indexed_db::TRANSACTION_READ_WRITE && - DoScopesOverlap(transaction->scope(), other->scope())) - return false; - } - return true; + return !DoSetsIntersect(transaction->scope(), locked_scope); } NOTREACHED(); return false; diff --git a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h index d9f9cd53266..e123a4e7383 100644 --- a/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h +++ b/chromium/content/browser/indexed_db/indexed_db_transaction_coordinator.h @@ -37,7 +37,8 @@ class IndexedDBTransactionCoordinator { private: void ProcessStartedTransactions(); - bool CanRunTransaction(IndexedDBTransaction* transaction); + bool CanRunTransaction(IndexedDBTransaction* const transaction, + const std::set<int64>& locked_scope) const; // This is just an efficient way to keep references to all transactions. std::map<IndexedDBTransaction*, scoped_refptr<IndexedDBTransaction> > diff --git a/chromium/content/browser/indexed_db/leveldb/avltree.h b/chromium/content/browser/indexed_db/leveldb/avltree.h deleted file mode 100644 index 91df04868c7..00000000000 --- a/chromium/content/browser/indexed_db/leveldb/avltree.h +++ /dev/null @@ -1,977 +0,0 @@ -// Copyright (c) 2013 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. - -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Based on Abstract AVL Tree Template v1.5 by Walt Karas - * <http://geocities.com/wkaras/gen_cpp/avl_tree.html>. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_AVLTREE_H_ -#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_AVLTREE_H_ - -#include "base/logging.h" -#include "content/browser/indexed_db/leveldb/fixed_array.h" - -namespace content { - -// Here is the reference class for BSet. -// -// class BSet -// { -// public: -// -// class ANY_bitref -// { -// public: -// operator bool (); -// void operator = (bool b); -// }; -// -// // Does not have to initialize bits. -// BSet(); -// -// // Must return a valid value for index when 0 <= index < maxDepth -// ANY_bitref operator [] (unsigned index); -// -// // Set all bits to 1. -// void Set(); -// -// // Set all bits to 0. -// void Reset(); -// }; - -template <unsigned maxDepth> -class AVLTreeDefaultBSet { - public: - bool& operator[](unsigned i) { -#if defined(ADDRESS_SANITIZER) - CHECK(i < maxDepth); -#endif - return data_[i]; - } - void Set() { - for (unsigned i = 0; i < maxDepth; ++i) - data_[i] = true; - } - void Reset() { - for (unsigned i = 0; i < maxDepth; ++i) - data_[i] = false; - } - - private: - FixedArray<bool, maxDepth> data_; -}; - -// How to determine maxDepth: -// d Minimum number of nodes -// 2 2 -// 3 4 -// 4 7 -// 5 12 -// 6 20 -// 7 33 -// 8 54 -// 9 88 -// 10 143 -// 11 232 -// 12 376 -// 13 609 -// 14 986 -// 15 1,596 -// 16 2,583 -// 17 4,180 -// 18 6,764 -// 19 10,945 -// 20 17,710 -// 21 28,656 -// 22 46,367 -// 23 75,024 -// 24 121,392 -// 25 196,417 -// 26 317,810 -// 27 514,228 -// 28 832,039 -// 29 1,346,268 -// 30 2,178,308 -// 31 3,524,577 -// 32 5,702,886 -// 33 9,227,464 -// 34 14,930,351 -// 35 24,157,816 -// 36 39,088,168 -// 37 63,245,985 -// 38 102,334,154 -// 39 165,580,140 -// 40 267,914,295 -// 41 433,494,436 -// 42 701,408,732 -// 43 1,134,903,169 -// 44 1,836,311,902 -// 45 2,971,215,072 -// -// E.g., if, in a particular instantiation, the maximum number of nodes in a -// tree instance is 1,000,000, the maximum depth should be 28. -// You pick 28 because MN(28) is 832,039, which is less than or equal to -// 1,000,000, and MN(29) is 1,346,268, which is strictly greater than 1,000,000. - -template <class Abstractor, - unsigned maxDepth = 32, - class BSet = AVLTreeDefaultBSet<maxDepth> > -class AVLTree { - public: - typedef typename Abstractor::key key; - typedef typename Abstractor::handle handle; - typedef typename Abstractor::size size; - - enum SearchType { - EQUAL = 1, - LESS = 2, - GREATER = 4, - LESS_EQUAL = EQUAL | LESS, - GREATER_EQUAL = EQUAL | GREATER - }; - - Abstractor& abstractor() { return abs_; } - - inline handle Insert(handle h); - - inline handle Search(key k, SearchType st = EQUAL); - inline handle SearchLeast(); - inline handle SearchGreatest(); - - inline handle Remove(key k); - - inline handle Subst(handle new_node); - - void Purge() { abs_.root = Null(); } - - bool IsEmpty() { return abs_.root == Null(); } - - AVLTree() { abs_.root = Null(); } - - class Iterator { - public: - // Initialize depth to invalid value, to indicate iterator is - // invalid. (Depth is zero-base.) - Iterator() { depth_ = ~0U; } - - void StartIter(AVLTree* tree, key k, SearchType st = EQUAL) { - // Mask of high bit in an int. - const int kMaskHighBit = static_cast<int>(~((~(unsigned)0) >> 1)); - - // Save the tree that we're going to iterate through in a - // member variable. - tree_ = tree; - - int cmp, target_cmp; - handle h = tree_->abs_.root; - unsigned d = 0; - - depth_ = ~0U; - - if (h == Null()) { - // Tree is empty. - return; - } - - if (st & LESS) { - // Key can be greater than key of starting node. - target_cmp = 1; - } else if (st & GREATER) { - // Key can be less than key of starting node. - target_cmp = -1; - } else { - // Key must be same as key of starting node. - target_cmp = 0; - } - - for (;;) { - cmp = CmpKN(k, h); - if (cmp == 0) { - if (st & EQUAL) { - // Equal node was sought and found as starting node. - depth_ = d; - break; - } - cmp = -target_cmp; - } else if (target_cmp != 0) { - if (!((cmp ^ target_cmp) & kMaskHighBit)) { - // cmp and target_cmp are both negative or both positive. - depth_ = d; - } - } - h = cmp < 0 ? GetLT(h) : GetGT(h); - if (h == Null()) - break; - branch_[d] = cmp > 0; - path_h_[d++] = h; - } - } - - void StartIterLeast(AVLTree* tree) { - tree_ = tree; - - handle h = tree_->abs_.root; - - depth_ = ~0U; - - branch_.Reset(); - - while (h != Null()) { - if (depth_ != ~0U) - path_h_[depth_] = h; - depth_++; - h = GetLT(h); - } - } - - void StartIterGreatest(AVLTree* tree) { - tree_ = tree; - - handle h = tree_->abs_.root; - - depth_ = ~0U; - - branch_.Set(); - - while (h != Null()) { - if (depth_ != ~0U) - path_h_[depth_] = h; - depth_++; - h = GetGT(h); - } - } - - handle operator*() { - if (depth_ == ~0U) - return Null(); - - return depth_ == 0 ? tree_->abs_.root : path_h_[depth_ - 1]; - } - - void operator++() { - if (depth_ != ~0U) { - handle h = GetGT(**this); - if (h == Null()) { - do { - if (depth_ == 0) { - depth_ = ~0U; - break; - } - depth_--; - } while (branch_[depth_]); - } else { - branch_[depth_] = true; - path_h_[depth_++] = h; - for (;;) { - h = GetLT(h); - if (h == Null()) - break; - branch_[depth_] = false; - path_h_[depth_++] = h; - } - } - } - } - - void operator--() { - if (depth_ != ~0U) { - handle h = GetLT(**this); - if (h == Null()) { - do { - if (depth_ == 0) { - depth_ = ~0U; - break; - } - depth_--; - } while (!branch_[depth_]); - } else { - branch_[depth_] = false; - path_h_[depth_++] = h; - for (;;) { - h = GetGT(h); - if (h == Null()) - break; - branch_[depth_] = true; - path_h_[depth_++] = h; - } - } - } - } - - void operator++(int /*ignored*/) { ++(*this); } - void operator--(int /*ignored*/) { --(*this); } - - protected: - // Tree being iterated over. - AVLTree* tree_; - - // Records a path into the tree. If branch_[n] is true, indicates - // take greater branch from the nth node in the path, otherwise - // take the less branch. branch_[0] gives branch from root, and - // so on. - BSet branch_; - - // Zero-based depth of path into tree. - unsigned depth_; - - // Handles of nodes in path from root to current node (returned by *). - static const size_t kPathSize = maxDepth - 1; - handle path_h_[kPathSize]; - - int CmpKN(key k, handle h) { return tree_->abs_.CompareKeyNode(k, h); } - int CmpNN(handle h1, handle h2) { - return tree_->abs_.CompareNodeNode(h1, h2); - } - handle GetLT(handle h) { return tree_->abs_.GetLess(h); } - handle GetGT(handle h) { return tree_->abs_.GetGreater(h); } - handle Null() { return tree_->abs_.Null(); } - }; - - template <typename fwd_iter> - bool Build(fwd_iter p, size num_nodes) { - if (num_nodes == 0) { - abs_.root = Null(); - return true; - } - - // Gives path to subtree being built. If branch[N] is false, branch - // less from the node at depth N, if true branch greater. - BSet branch; - - // If rem[N] is true, then for the current subtree at depth N, it's - // greater subtree has one more node than it's less subtree. - BSet rem; - - // Depth of root node of current subtree. - unsigned depth = 0; - - // Number of nodes in current subtree. - size num_sub = num_nodes; - - // The algorithm relies on a stack of nodes whose less subtree has - // been built, but whose right subtree has not yet been built. The - // stack is implemented as linked list. The nodes are linked - // together by having the "greater" handle of a node set to the - // next node in the list. "less_parent" is the handle of the first - // node in the list. - handle less_parent = Null(); - - // h is root of current subtree, child is one of its children. - handle h, child; - - for (;;) { - while (num_sub > 2) { - // Subtract one for root of subtree. - num_sub--; - rem[depth] = !!(num_sub & 1); - branch[depth++] = false; - num_sub >>= 1; - } - - if (num_sub == 2) { - // Build a subtree with two nodes, slanting to greater. - // I arbitrarily chose to always have the extra node in the - // greater subtree when there is an odd number of nodes to - // split between the two subtrees. - - h = *p; - p++; - child = *p; - p++; - SetLT(child, Null()); - SetGT(child, Null()); - SetBF(child, 0); - SetGT(h, child); - SetLT(h, Null()); - SetBF(h, 1); - } else { // num_sub == 1 - // Build a subtree with one node. - - h = *p; - p++; - SetLT(h, Null()); - SetGT(h, Null()); - SetBF(h, 0); - } - - while (depth) { - depth--; - if (!branch[depth]) { - // We've completed a less subtree. - break; - } - - // We've completed a greater subtree, so attach it to - // its parent (that is less than it). We pop the parent - // off the stack of less parents. - child = h; - h = less_parent; - less_parent = GetGT(h); - SetGT(h, child); - // num_sub = 2 * (num_sub - rem[depth]) + rem[depth] + 1 - num_sub <<= 1; - num_sub += 1 - rem[depth]; - if (num_sub & (num_sub - 1)) { - // num_sub is not a power of 2 - SetBF(h, 0); - } else { - // num_sub is a power of 2 - SetBF(h, 1); - } - } - - if (num_sub == num_nodes) { - // We've completed the full tree. - break; - } - - // The subtree we've completed is the less subtree of the - // next node in the sequence. - - child = h; - h = *p; - p++; - SetLT(h, child); - - // Put h into stack of less parents. - SetGT(h, less_parent); - less_parent = h; - - // Proceed to creating greater than subtree of h. - branch[depth] = true; - num_sub += rem[depth++]; - } // end for (;;) - - abs_.root = h; - - return true; - } - - protected: - friend class Iterator; - - // Create a class whose sole purpose is to take advantage of - // the "empty member" optimization. - struct abs_plus_root : public Abstractor { - // The handle of the root element in the AVL tree. - handle root; - }; - - abs_plus_root abs_; - - handle GetLT(handle h) { return abs_.GetLess(h); } - void SetLT(handle h, handle lh) { abs_.SetLess(h, lh); } - - handle GetGT(handle h) { return abs_.GetGreater(h); } - void SetGT(handle h, handle gh) { abs_.SetGreater(h, gh); } - - int GetBF(handle h) { return abs_.GetBalanceFactor(h); } - void SetBF(handle h, int bf) { abs_.SetBalanceFactor(h, bf); } - - int CmpKN(key k, handle h) { return abs_.CompareKeyNode(k, h); } - int CmpNN(handle h1, handle h2) { return abs_.CompareNodeNode(h1, h2); } - - handle Null() { return abs_.Null(); } - - private: - // Balances subtree, returns handle of root node of subtree - // after balancing. - handle Balance(handle bal_h) { - handle deep_h; - - // Either the "greater than" or the "less than" subtree of - // this node has to be 2 levels deeper (or else it wouldn't - // need balancing). - - if (GetBF(bal_h) > 0) { - // "Greater than" subtree is deeper. - - deep_h = GetGT(bal_h); - - if (GetBF(deep_h) < 0) { - handle old_h = bal_h; - bal_h = GetLT(deep_h); - - SetGT(old_h, GetLT(bal_h)); - SetLT(deep_h, GetGT(bal_h)); - SetLT(bal_h, old_h); - SetGT(bal_h, deep_h); - - int bf = GetBF(bal_h); - if (bf != 0) { - if (bf > 0) { - SetBF(old_h, -1); - SetBF(deep_h, 0); - } else { - SetBF(deep_h, 1); - SetBF(old_h, 0); - } - SetBF(bal_h, 0); - } else { - SetBF(old_h, 0); - SetBF(deep_h, 0); - } - } else { - SetGT(bal_h, GetLT(deep_h)); - SetLT(deep_h, bal_h); - if (GetBF(deep_h) == 0) { - SetBF(deep_h, -1); - SetBF(bal_h, 1); - } else { - SetBF(deep_h, 0); - SetBF(bal_h, 0); - } - bal_h = deep_h; - } - } else { - // "Less than" subtree is deeper. - - deep_h = GetLT(bal_h); - - if (GetBF(deep_h) > 0) { - handle old_h = bal_h; - bal_h = GetGT(deep_h); - SetLT(old_h, GetGT(bal_h)); - SetGT(deep_h, GetLT(bal_h)); - SetGT(bal_h, old_h); - SetLT(bal_h, deep_h); - - int bf = GetBF(bal_h); - if (bf != 0) { - if (bf < 0) { - SetBF(old_h, 1); - SetBF(deep_h, 0); - } else { - SetBF(deep_h, -1); - SetBF(old_h, 0); - } - SetBF(bal_h, 0); - } else { - SetBF(old_h, 0); - SetBF(deep_h, 0); - } - } else { - SetLT(bal_h, GetGT(deep_h)); - SetGT(deep_h, bal_h); - if (GetBF(deep_h) == 0) { - SetBF(deep_h, 1); - SetBF(bal_h, -1); - } else { - SetBF(deep_h, 0); - SetBF(bal_h, 0); - } - bal_h = deep_h; - } - } - - return bal_h; - } -}; - -template <class Abstractor, unsigned maxDepth, class BSet> -inline typename AVLTree<Abstractor, maxDepth, BSet>::handle -AVLTree<Abstractor, maxDepth, BSet>::Insert(handle h) { - SetLT(h, Null()); - SetGT(h, Null()); - SetBF(h, 0); - - if (abs_.root == Null()) { - abs_.root = h; - } else { - // Last unbalanced node encountered in search for insertion point. - handle unbal = Null(); - // Parent of last unbalanced node. - handle parent_unbal = Null(); - // Balance factor of last unbalanced node. - int unbal_bf; - - // Zero-based depth in tree. - unsigned depth = 0, unbal_depth = 0; - - // Records a path into the tree. If branch[n] is true, indicates - // take greater branch from the nth node in the path, otherwise - // take the less branch. branch[0] gives branch from root, and - // so on. - BSet branch; - - handle hh = abs_.root; - handle parent = Null(); - int cmp; - - do { - if (GetBF(hh) != 0) { - unbal = hh; - parent_unbal = parent; - unbal_depth = depth; - } - cmp = CmpNN(h, hh); - if (cmp == 0) { - // Duplicate key. - return hh; - } - parent = hh; - hh = cmp < 0 ? GetLT(hh) : GetGT(hh); - branch[depth++] = cmp > 0; - } while (hh != Null()); - - // Add node to insert as leaf of tree. - if (cmp < 0) - SetLT(parent, h); - else - SetGT(parent, h); - - depth = unbal_depth; - - if (unbal == Null()) { - hh = abs_.root; - } else { - cmp = branch[depth++] ? 1 : -1; - unbal_bf = GetBF(unbal); - if (cmp < 0) - unbal_bf--; - else // cmp > 0 - unbal_bf++; - hh = cmp < 0 ? GetLT(unbal) : GetGT(unbal); - if ((unbal_bf != -2) && (unbal_bf != 2)) { - // No rebalancing of tree is necessary. - SetBF(unbal, unbal_bf); - unbal = Null(); - } - } - - if (hh != Null()) { - while (h != hh) { - cmp = branch[depth++] ? 1 : -1; - if (cmp < 0) { - SetBF(hh, -1); - hh = GetLT(hh); - } else { // cmp > 0 - SetBF(hh, 1); - hh = GetGT(hh); - } - } - } - - if (unbal != Null()) { - unbal = Balance(unbal); - if (parent_unbal == Null()) { - abs_.root = unbal; - } else { - depth = unbal_depth - 1; - cmp = branch[depth] ? 1 : -1; - if (cmp < 0) - SetLT(parent_unbal, unbal); - else // cmp > 0 - SetGT(parent_unbal, unbal); - } - } - } - - return h; -} - -template <class Abstractor, unsigned maxDepth, class BSet> -inline typename AVLTree<Abstractor, maxDepth, BSet>::handle -AVLTree<Abstractor, maxDepth, BSet>::Search( - key k, - typename AVLTree<Abstractor, maxDepth, BSet>::SearchType st) { - const int kMaskHighBit = static_cast<int>(~((~(unsigned)0) >> 1)); - - int cmp, target_cmp; - handle match_h = Null(); - handle h = abs_.root; - - if (st & LESS) - target_cmp = 1; - else if (st & GREATER) - target_cmp = -1; - else - target_cmp = 0; - - while (h != Null()) { - cmp = CmpKN(k, h); - if (cmp == 0) { - if (st & EQUAL) { - match_h = h; - break; - } - cmp = -target_cmp; - } else if (target_cmp != 0) { - if (!((cmp ^ target_cmp) & kMaskHighBit)) { - // cmp and target_cmp are both positive or both negative. - match_h = h; - } - } - h = cmp < 0 ? GetLT(h) : GetGT(h); - } - - return match_h; -} - -template <class Abstractor, unsigned maxDepth, class BSet> -inline typename AVLTree<Abstractor, maxDepth, BSet>::handle -AVLTree<Abstractor, maxDepth, BSet>::SearchLeast() { - handle h = abs_.root, parent = Null(); - - while (h != Null()) { - parent = h; - h = GetLT(h); - } - - return parent; -} - -template <class Abstractor, unsigned maxDepth, class BSet> -inline typename AVLTree<Abstractor, maxDepth, BSet>::handle -AVLTree<Abstractor, maxDepth, BSet>::SearchGreatest() { - handle h = abs_.root, parent = Null(); - - while (h != Null()) { - parent = h; - h = GetGT(h); - } - - return parent; -} - -template <class Abstractor, unsigned maxDepth, class BSet> -inline typename AVLTree<Abstractor, maxDepth, BSet>::handle -AVLTree<Abstractor, maxDepth, BSet>::Remove(key k) { - // Zero-based depth in tree. - unsigned depth = 0, rm_depth; - - // Records a path into the tree. If branch[n] is true, indicates - // take greater branch from the nth node in the path, otherwise - // take the less branch. branch[0] gives branch from root, and - // so on. - BSet branch; - - handle h = abs_.root; - handle parent = Null(), child; - int cmp, cmp_shortened_sub_with_path = 0; - - for (;;) { - if (h == Null()) { - // No node in tree with given key. - return Null(); - } - cmp = CmpKN(k, h); - if (cmp == 0) { - // Found node to remove. - break; - } - parent = h; - h = cmp < 0 ? GetLT(h) : GetGT(h); - branch[depth++] = cmp > 0; - cmp_shortened_sub_with_path = cmp; - } - handle rm = h; - handle parent_rm = parent; - rm_depth = depth; - - // If the node to remove is not a leaf node, we need to get a - // leaf node, or a node with a single leaf as its child, to put - // in the place of the node to remove. We will get the greatest - // node in the less subtree (of the node to remove), or the least - // node in the greater subtree. We take the leaf node from the - // deeper subtree, if there is one. - - if (GetBF(h) < 0) { - child = GetLT(h); - branch[depth] = false; - cmp = -1; - } else { - child = GetGT(h); - branch[depth] = true; - cmp = 1; - } - depth++; - - if (child != Null()) { - cmp = -cmp; - do { - parent = h; - h = child; - if (cmp < 0) { - child = GetLT(h); - branch[depth] = false; - } else { - child = GetGT(h); - branch[depth] = true; - } - depth++; - } while (child != Null()); - - if (parent == rm) { - // Only went through do loop once. Deleted node will be replaced - // in the tree structure by one of its immediate children. - cmp_shortened_sub_with_path = -cmp; - } else { - cmp_shortened_sub_with_path = cmp; - } - - // Get the handle of the opposite child, which may not be null. - child = cmp > 0 ? GetLT(h) : GetGT(h); - } - - if (parent == Null()) { - // There were only 1 or 2 nodes in this tree. - abs_.root = child; - } else if (cmp_shortened_sub_with_path < 0) { - SetLT(parent, child); - } else { - SetGT(parent, child); - } - - // "path" is the parent of the subtree being eliminated or reduced - // from a depth of 2 to 1. If "path" is the node to be removed, we - // set path to the node we're about to poke into the position of the - // node to be removed. - handle path = parent == rm ? h : parent; - - if (h != rm) { - // Poke in the replacement for the node to be removed. - SetLT(h, GetLT(rm)); - SetGT(h, GetGT(rm)); - SetBF(h, GetBF(rm)); - if (parent_rm == Null()) { - abs_.root = h; - } else { - depth = rm_depth - 1; - if (branch[depth]) - SetGT(parent_rm, h); - else - SetLT(parent_rm, h); - } - } - - if (path != Null()) { - // Create a temporary linked list from the parent of the path node - // to the root node. - h = abs_.root; - parent = Null(); - depth = 0; - while (h != path) { - if (branch[depth++]) { - child = GetGT(h); - SetGT(h, parent); - } else { - child = GetLT(h); - SetLT(h, parent); - } - parent = h; - h = child; - } - - // Climb from the path node to the root node using the linked - // list, restoring the tree structure and rebalancing as necessary. - bool reduced_depth = true; - int bf; - cmp = cmp_shortened_sub_with_path; - for (;;) { - if (reduced_depth) { - bf = GetBF(h); - if (cmp < 0) - bf++; - else // cmp > 0 - bf--; - if ((bf == -2) || (bf == 2)) { - h = Balance(h); - bf = GetBF(h); - } else { - SetBF(h, bf); - } - reduced_depth = (bf == 0); - } - if (parent == Null()) - break; - child = h; - h = parent; - cmp = branch[--depth] ? 1 : -1; - if (cmp < 0) { - parent = GetLT(h); - SetLT(h, child); - } else { - parent = GetGT(h); - SetGT(h, child); - } - } - abs_.root = h; - } - - return rm; -} - -template <class Abstractor, unsigned maxDepth, class BSet> -inline typename AVLTree<Abstractor, maxDepth, BSet>::handle -AVLTree<Abstractor, maxDepth, BSet>::Subst(handle new_node) { - handle h = abs_.root; - handle parent = Null(); - int cmp, last_cmp; - - // Search for node already in tree with same key. - for (;;) { - if (h == Null()) { - // No node in tree with same key as new node. - return Null(); - } - cmp = CmpNN(new_node, h); - if (cmp == 0) { - // Found the node to substitute new one for. - break; - } - last_cmp = cmp; - parent = h; - h = cmp < 0 ? GetLT(h) : GetGT(h); - } - - // Copy tree housekeeping fields from node in tree to new node. - SetLT(new_node, GetLT(h)); - SetGT(new_node, GetGT(h)); - SetBF(new_node, GetBF(h)); - - if (parent == Null()) { - // New node is also new root. - abs_.root = new_node; - } else { - // Make parent point to new node. - if (last_cmp < 0) - SetLT(parent, new_node); - else - SetGT(parent, new_node); - } - - return h; -} - -} // namespace content - -#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_AVLTREE_H_ diff --git a/chromium/content/browser/indexed_db/leveldb/fixed_array.h b/chromium/content/browser/indexed_db/leveldb/fixed_array.h deleted file mode 100644 index cef43973fb0..00000000000 --- a/chromium/content/browser/indexed_db/leveldb/fixed_array.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2013 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. - -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_FIXED_ARRAY_H_ -#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_FIXED_ARRAY_H_ - -#include "base/logging.h" - -namespace content { - -template <typename T, size_t Size> -class FixedArray { - public: - T& operator[](size_t i) { -#if defined(ADDRESS_SANITIZER) - CHECK(i < Size); -#endif - return data_[i]; - } - - const T& operator[](size_t i) const { -#if defined(ADDRESS_SANITIZER) - CHECK(i < Size); -#endif - return data_[i]; - } - - T* data() { return data_; } - size_t size() const { return Size; } - - private: - T data_[Size]; -}; - -} // namespace content - -#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_FIXED_ARRAY_H_ diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc b/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc index 03d8ca6ccf0..cab8473a7a3 100644 --- a/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc +++ b/chromium/content/browser/indexed_db/leveldb/leveldb_database.cc @@ -121,9 +121,9 @@ static int CheckFreeSpace(const char* const type, base::HistogramBase::kUmaTargetedHistogramFlag)->Add(1 /*sample*/); return -1; } - int clamped_disk_space_k_bytes = - free_disk_space_in_k_bytes > INT_MAX ? INT_MAX - : free_disk_space_in_k_bytes; + int clamped_disk_space_k_bytes = free_disk_space_in_k_bytes > INT_MAX + ? INT_MAX + : free_disk_space_in_k_bytes; const uint64 histogram_max = static_cast<uint64>(1e9); COMPILE_ASSERT(histogram_max <= INT_MAX, histogram_max_too_big); base::Histogram::FactoryGet(name, @@ -268,11 +268,10 @@ static void HistogramLevelDBError(const std::string& histogram_name, ParseAndHistogramCorruptionDetails(histogram_name, s); } -leveldb::Status LevelDBDatabase::Open( - const base::FilePath& file_name, - const LevelDBComparator* comparator, - scoped_ptr<LevelDBDatabase>* result, - bool* is_disk_full) { +leveldb::Status LevelDBDatabase::Open(const base::FilePath& file_name, + const LevelDBComparator* comparator, + scoped_ptr<LevelDBDatabase>* result, + bool* is_disk_full) { scoped_ptr<ComparatorAdapter> comparator_adapter( new ComparatorAdapter(comparator)); diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_database.h b/chromium/content/browser/indexed_db/leveldb/leveldb_database.h index 1efeefa2d17..5732ac4e3d6 100644 --- a/chromium/content/browser/indexed_db/leveldb/leveldb_database.h +++ b/chromium/content/browser/indexed_db/leveldb/leveldb_database.h @@ -6,7 +6,6 @@ #define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_DATABASE_H_ #include <string> -#include <vector> #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc index 5388d44c6f5..0c3619465bb 100644 --- a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc +++ b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.cc @@ -14,49 +14,43 @@ using base::StringPiece; namespace content { LevelDBTransaction::LevelDBTransaction(LevelDBDatabase* db) - : db_(db), snapshot_(db), comparator_(db->Comparator()), finished_(false) { - tree_.abstractor().comparator_ = comparator_; -} - -LevelDBTransaction::AVLTreeNode::AVLTreeNode() {} -LevelDBTransaction::AVLTreeNode::~AVLTreeNode() {} - -void LevelDBTransaction::ClearTree() { - TreeType::Iterator iterator; - iterator.StartIterLeast(&tree_); - - std::vector<AVLTreeNode*> nodes; - - while (*iterator) { - nodes.push_back(*iterator); - ++iterator; + : db_(db), + snapshot_(db), + comparator_(db->Comparator()), + data_comparator_(comparator_), + data_(data_comparator_), + finished_(false) {} + +LevelDBTransaction::Record::Record() : deleted(false) {} +LevelDBTransaction::Record::~Record() {} + +void LevelDBTransaction::Clear() { + for (DataType::iterator it = data_.begin(); it != data_.end(); ++it) { + delete it->second; } - tree_.Purge(); - - for (size_t i = 0; i < nodes.size(); ++i) - delete nodes[i]; + data_.clear(); } -LevelDBTransaction::~LevelDBTransaction() { ClearTree(); } +LevelDBTransaction::~LevelDBTransaction() { Clear(); } void LevelDBTransaction::Set(const StringPiece& key, std::string* value, bool deleted) { DCHECK(!finished_); - bool new_node = false; - AVLTreeNode* node = tree_.Search(key); - - if (!node) { - node = new AVLTreeNode; - node->key = key.as_string(); - tree_.Insert(node); - new_node = true; + DataType::iterator it = data_.find(key); + + if (it == data_.end()) { + Record* record = new Record(); + record->key.assign(key.begin(), key.end() - key.begin()); + record->value.swap(*value); + record->deleted = deleted; + data_[record->key] = record; + NotifyIterators(); + return; } - node->value.swap(*value); - node->deleted = deleted; - if (new_node) - NotifyIteratorsOfTreeChange(); + it->second->value.swap(*value); + it->second->deleted = deleted; } void LevelDBTransaction::Put(const StringPiece& key, std::string* value) { @@ -73,13 +67,14 @@ bool LevelDBTransaction::Get(const StringPiece& key, bool* found) { *found = false; DCHECK(!finished_); - AVLTreeNode* node = tree_.Search(key); + std::string string_key(key.begin(), key.end() - key.begin()); + DataType::const_iterator it = data_.find(string_key); - if (node) { - if (node->deleted) + if (it != data_.end()) { + if (it->second->deleted) return true; - *value = node->value; + *value = it->second->value; *found = true; return true; } @@ -95,29 +90,25 @@ bool LevelDBTransaction::Get(const StringPiece& key, bool LevelDBTransaction::Commit() { DCHECK(!finished_); - if (tree_.IsEmpty()) { + if (data_.empty()) { finished_ = true; return true; } scoped_ptr<LevelDBWriteBatch> write_batch = LevelDBWriteBatch::Create(); - TreeType::Iterator iterator; - iterator.StartIterLeast(&tree_); - - while (*iterator) { - AVLTreeNode* node = *iterator; - if (!node->deleted) - write_batch->Put(node->key, node->value); + for (DataType::iterator iterator = data_.begin(); iterator != data_.end(); + ++iterator) { + if (!iterator->second->deleted) + write_batch->Put(iterator->first, iterator->second->value); else - write_batch->Remove(node->key); - ++iterator; + write_batch->Remove(iterator->first); } if (!db_->Write(*write_batch)) return false; - ClearTree(); + Clear(); finished_ = true; return true; } @@ -125,80 +116,66 @@ bool LevelDBTransaction::Commit() { void LevelDBTransaction::Rollback() { DCHECK(!finished_); finished_ = true; - ClearTree(); + Clear(); } scoped_ptr<LevelDBIterator> LevelDBTransaction::CreateIterator() { return TransactionIterator::Create(this).PassAs<LevelDBIterator>(); } -scoped_ptr<LevelDBTransaction::TreeIterator> -LevelDBTransaction::TreeIterator::Create(LevelDBTransaction* transaction) { - return make_scoped_ptr(new TreeIterator(transaction)); +scoped_ptr<LevelDBTransaction::DataIterator> +LevelDBTransaction::DataIterator::Create(LevelDBTransaction* transaction) { + return make_scoped_ptr(new DataIterator(transaction)); } -bool LevelDBTransaction::TreeIterator::IsValid() const { return !!*iterator_; } - -void LevelDBTransaction::TreeIterator::SeekToLast() { - iterator_.StartIterGreatest(tree_); - if (IsValid()) - key_ = (*iterator_)->key; +bool LevelDBTransaction::DataIterator::IsValid() const { + return iterator_ != data_->end(); } -void LevelDBTransaction::TreeIterator::Seek(const StringPiece& target) { - iterator_.StartIter(tree_, target, TreeType::EQUAL); - if (!IsValid()) - iterator_.StartIter(tree_, target, TreeType::GREATER); +void LevelDBTransaction::DataIterator::SeekToLast() { + iterator_ = data_->end(); + if (iterator_ != data_->begin()) + --iterator_; +} - if (IsValid()) - key_ = (*iterator_)->key; +void LevelDBTransaction::DataIterator::Seek(const StringPiece& target) { + iterator_ = data_->lower_bound(target); } -void LevelDBTransaction::TreeIterator::Next() { +void LevelDBTransaction::DataIterator::Next() { DCHECK(IsValid()); ++iterator_; - if (IsValid()) { - DCHECK_GE(transaction_->comparator_->Compare((*iterator_)->key, key_), 0); - key_ = (*iterator_)->key; - } } -void LevelDBTransaction::TreeIterator::Prev() { +void LevelDBTransaction::DataIterator::Prev() { DCHECK(IsValid()); - --iterator_; - if (IsValid()) { - DCHECK_LT(tree_->abstractor().comparator_->Compare((*iterator_)->key, key_), - 0); - key_ = (*iterator_)->key; - } + if (iterator_ != data_->begin()) + --iterator_; + else + iterator_ = data_->end(); } -StringPiece LevelDBTransaction::TreeIterator::Key() const { +StringPiece LevelDBTransaction::DataIterator::Key() const { DCHECK(IsValid()); - return key_; + return iterator_->first; } -StringPiece LevelDBTransaction::TreeIterator::Value() const { +StringPiece LevelDBTransaction::DataIterator::Value() const { DCHECK(IsValid()); DCHECK(!IsDeleted()); - return (*iterator_)->value; + return iterator_->second->value; } -bool LevelDBTransaction::TreeIterator::IsDeleted() const { +bool LevelDBTransaction::DataIterator::IsDeleted() const { DCHECK(IsValid()); - return (*iterator_)->deleted; + return iterator_->second->deleted; } -void LevelDBTransaction::TreeIterator::Reset() { - DCHECK(IsValid()); - iterator_.StartIter(tree_, key_, TreeType::EQUAL); - DCHECK(IsValid()); -} +LevelDBTransaction::DataIterator::~DataIterator() {} -LevelDBTransaction::TreeIterator::~TreeIterator() {} - -LevelDBTransaction::TreeIterator::TreeIterator(LevelDBTransaction* transaction) - : tree_(&transaction->tree_), transaction_(transaction) {} +LevelDBTransaction::DataIterator::DataIterator(LevelDBTransaction* transaction) + : data_(&transaction->data_), + iterator_(data_->end()) {} scoped_ptr<LevelDBTransaction::TransactionIterator> LevelDBTransaction::TransactionIterator::Create( @@ -210,11 +187,11 @@ LevelDBTransaction::TransactionIterator::TransactionIterator( scoped_refptr<LevelDBTransaction> transaction) : transaction_(transaction), comparator_(transaction_->comparator_), - tree_iterator_(TreeIterator::Create(transaction_.get())), + data_iterator_(DataIterator::Create(transaction_.get())), db_iterator_(transaction_->db_->CreateIterator(&transaction_->snapshot_)), current_(0), direction_(FORWARD), - tree_changed_(false) { + data_changed_(false) { transaction_->RegisterIterator(this); } @@ -227,7 +204,7 @@ bool LevelDBTransaction::TransactionIterator::IsValid() const { } void LevelDBTransaction::TransactionIterator::SeekToLast() { - tree_iterator_->SeekToLast(); + data_iterator_->SeekToLast(); db_iterator_->SeekToLast(); direction_ = REVERSE; @@ -236,7 +213,7 @@ void LevelDBTransaction::TransactionIterator::SeekToLast() { } void LevelDBTransaction::TransactionIterator::Seek(const StringPiece& target) { - tree_iterator_->Seek(target); + data_iterator_->Seek(target); db_iterator_->Seek(target); direction_ = FORWARD; @@ -246,22 +223,23 @@ void LevelDBTransaction::TransactionIterator::Seek(const StringPiece& target) { void LevelDBTransaction::TransactionIterator::Next() { DCHECK(IsValid()); - if (tree_changed_) - RefreshTreeIterator(); + if (data_changed_) + RefreshDataIterator(); if (direction_ != FORWARD) { // Ensure the non-current iterator is positioned after Key(). LevelDBIterator* non_current = (current_ == db_iterator_.get()) - ? tree_iterator_.get() + ? data_iterator_.get() : db_iterator_.get(); non_current->Seek(Key()); if (non_current->IsValid() && - !comparator_->Compare(non_current->Key(), Key())) - non_current->Next(); // Take an extra step so the non-current key is - // strictly greater than Key(). - + !comparator_->Compare(non_current->Key(), Key())) { + // Take an extra step so the non-current key is + // strictly greater than Key(). + non_current->Next(); + } DCHECK(!non_current->IsValid() || comparator_->Compare(non_current->Key(), Key()) > 0); @@ -275,14 +253,14 @@ void LevelDBTransaction::TransactionIterator::Next() { void LevelDBTransaction::TransactionIterator::Prev() { DCHECK(IsValid()); - if (tree_changed_) - RefreshTreeIterator(); + if (data_changed_) + RefreshDataIterator(); if (direction_ != REVERSE) { // Ensure the non-current iterator is positioned before Key(). LevelDBIterator* non_current = (current_ == db_iterator_.get()) - ? tree_iterator_.get() + ? data_iterator_.get() : db_iterator_.get(); non_current->Seek(Key()); @@ -293,8 +271,8 @@ void LevelDBTransaction::TransactionIterator::Prev() { // stepping, like we do in Next() above. non_current->Prev(); } else { - non_current->SeekToLast(); // Iterator has no entries >= Key(). Position - // at last entry. + // Iterator has no entries >= Key(). Position at last entry. + non_current->SeekToLast(); } DCHECK(!non_current->IsValid() || comparator_->Compare(non_current->Key(), Key()) < 0); @@ -309,58 +287,58 @@ void LevelDBTransaction::TransactionIterator::Prev() { StringPiece LevelDBTransaction::TransactionIterator::Key() const { DCHECK(IsValid()); - if (tree_changed_) - RefreshTreeIterator(); + if (data_changed_) + RefreshDataIterator(); return current_->Key(); } StringPiece LevelDBTransaction::TransactionIterator::Value() const { DCHECK(IsValid()); - if (tree_changed_) - RefreshTreeIterator(); + if (data_changed_) + RefreshDataIterator(); return current_->Value(); } -void LevelDBTransaction::TransactionIterator::TreeChanged() { - tree_changed_ = true; +void LevelDBTransaction::TransactionIterator::DataChanged() { + data_changed_ = true; } -void LevelDBTransaction::TransactionIterator::RefreshTreeIterator() const { - DCHECK(tree_changed_); +void LevelDBTransaction::TransactionIterator::RefreshDataIterator() const { + DCHECK(data_changed_); - tree_changed_ = false; + data_changed_ = false; - if (tree_iterator_->IsValid() && tree_iterator_.get() == current_) { - tree_iterator_->Reset(); + if (data_iterator_->IsValid() && data_iterator_.get() == current_) { return; } if (db_iterator_->IsValid()) { - // There could be new nodes in the tree that we should iterate over. + // There could be new records in data that we should iterate over. if (direction_ == FORWARD) { - // Try to seek tree iterator to something greater than the db iterator. - tree_iterator_->Seek(db_iterator_->Key()); - if (tree_iterator_->IsValid() && - !comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key())) - tree_iterator_->Next(); // If equal, take another step so the tree - // iterator is strictly greater. + // Try to seek data iterator to something greater than the db iterator. + data_iterator_->Seek(db_iterator_->Key()); + if (data_iterator_->IsValid() && + !comparator_->Compare(data_iterator_->Key(), db_iterator_->Key())) { + // If equal, take another step so the data iterator is strictly greater. + data_iterator_->Next(); + } } else { // If going backward, seek to a key less than the db iterator. DCHECK_EQ(REVERSE, direction_); - tree_iterator_->Seek(db_iterator_->Key()); - if (tree_iterator_->IsValid()) - tree_iterator_->Prev(); + data_iterator_->Seek(db_iterator_->Key()); + if (data_iterator_->IsValid()) + data_iterator_->Prev(); } } } -bool LevelDBTransaction::TransactionIterator::TreeIteratorIsLower() const { - return comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key()) < 0; +bool LevelDBTransaction::TransactionIterator::DataIteratorIsLower() const { + return comparator_->Compare(data_iterator_->Key(), db_iterator_->Key()) < 0; } -bool LevelDBTransaction::TransactionIterator::TreeIteratorIsHigher() const { - return comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key()) > 0; +bool LevelDBTransaction::TransactionIterator::DataIteratorIsHigher() const { + return comparator_->Compare(data_iterator_->Key(), db_iterator_->Key()) > 0; } void LevelDBTransaction::TransactionIterator::HandleConflictsAndDeletes() { @@ -369,9 +347,9 @@ void LevelDBTransaction::TransactionIterator::HandleConflictsAndDeletes() { while (loop) { loop = false; - if (tree_iterator_->IsValid() && db_iterator_->IsValid() && - !comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key())) { - // For equal keys, the tree iterator takes precedence, so move the + if (data_iterator_->IsValid() && db_iterator_->IsValid() && + !comparator_->Compare(data_iterator_->Key(), db_iterator_->Key())) { + // For equal keys, the data iterator takes precedence, so move the // database iterator another step. if (direction_ == FORWARD) db_iterator_->Next(); @@ -379,16 +357,16 @@ void LevelDBTransaction::TransactionIterator::HandleConflictsAndDeletes() { db_iterator_->Prev(); } - // Skip over delete markers in the tree iterator until it catches up with + // Skip over delete markers in the data iterator until it catches up with // the db iterator. - if (tree_iterator_->IsValid() && tree_iterator_->IsDeleted()) { + if (data_iterator_->IsValid() && data_iterator_->IsDeleted()) { if (direction_ == FORWARD && - (!db_iterator_->IsValid() || TreeIteratorIsLower())) { - tree_iterator_->Next(); + (!db_iterator_->IsValid() || DataIteratorIsLower())) { + data_iterator_->Next(); loop = true; } else if (direction_ == REVERSE && - (!db_iterator_->IsValid() || TreeIteratorIsHigher())) { - tree_iterator_->Prev(); + (!db_iterator_->IsValid() || DataIteratorIsHigher())) { + data_iterator_->Prev(); loop = true; } } @@ -399,8 +377,8 @@ void LevelDBTransaction::TransactionIterator::SetCurrentIteratorToSmallestKey() { LevelDBIterator* smallest = 0; - if (tree_iterator_->IsValid()) - smallest = tree_iterator_.get(); + if (data_iterator_->IsValid()) + smallest = data_iterator_.get(); if (db_iterator_->IsValid()) { if (!smallest || @@ -414,8 +392,8 @@ LevelDBTransaction::TransactionIterator::SetCurrentIteratorToSmallestKey() { void LevelDBTransaction::TransactionIterator::SetCurrentIteratorToLargestKey() { LevelDBIterator* largest = 0; - if (tree_iterator_->IsValid()) - largest = tree_iterator_.get(); + if (data_iterator_->IsValid()) + largest = data_iterator_.get(); if (db_iterator_->IsValid()) { if (!largest || @@ -436,12 +414,12 @@ void LevelDBTransaction::UnregisterIterator(TransactionIterator* iterator) { iterators_.erase(iterator); } -void LevelDBTransaction::NotifyIteratorsOfTreeChange() { +void LevelDBTransaction::NotifyIterators() { for (std::set<TransactionIterator*>::iterator i = iterators_.begin(); i != iterators_.end(); ++i) { TransactionIterator* transaction_iterator = *i; - transaction_iterator->TreeChanged(); + transaction_iterator->DataChanged(); } } diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h index 83b4763edc1..b7b5d64df1e 100644 --- a/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h +++ b/chromium/content/browser/indexed_db/leveldb/leveldb_transaction.h @@ -5,14 +5,13 @@ #ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_ #define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_ +#include <map> #include <set> #include <string> -#include <vector> #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" -#include "content/browser/indexed_db/leveldb/avltree.h" #include "content/browser/indexed_db/leveldb/leveldb_comparator.h" #include "content/browser/indexed_db/leveldb/leveldb_database.h" #include "content/browser/indexed_db/leveldb/leveldb_iterator.h" @@ -38,53 +37,33 @@ class CONTENT_EXPORT LevelDBTransaction virtual ~LevelDBTransaction(); friend class base::RefCounted<LevelDBTransaction>; - struct AVLTreeNode { - AVLTreeNode(); - ~AVLTreeNode(); + struct Record { + Record(); + ~Record(); std::string key; std::string value; bool deleted; - - AVLTreeNode* less; - AVLTreeNode* greater; - int balance_factor; - DISALLOW_COPY_AND_ASSIGN(AVLTreeNode); }; - struct AVLTreeAbstractor { - typedef AVLTreeNode* handle; - typedef size_t size; - typedef base::StringPiece key; - - handle GetLess(handle h) { return h->less; } - void SetLess(handle h, handle less) { h->less = less; } - handle GetGreater(handle h) { return h->greater; } - void SetGreater(handle h, handle greater) { h->greater = greater; } - - int GetBalanceFactor(handle h) { return h->balance_factor; } - void SetBalanceFactor(handle h, int bf) { h->balance_factor = bf; } - - int CompareKeyKey(const key& ka, const key& kb) { - return comparator_->Compare(ka, kb); - } - int CompareKeyNode(const key& k, handle h) { - return CompareKeyKey(k, h->key); - } - int CompareNodeNode(handle ha, handle hb) { - return CompareKeyKey(ha->key, hb->key); + class Comparator { + public: + explicit Comparator(const LevelDBComparator* comparator) + : comparator_(comparator) {} + bool operator()(const base::StringPiece& a, + const base::StringPiece& b) const { + return comparator_->Compare(a, b) < 0; } - static handle Null() { return 0; } - + private: const LevelDBComparator* comparator_; }; - typedef AVLTree<AVLTreeAbstractor> TreeType; + typedef std::map<base::StringPiece, Record*, Comparator> DataType; - class TreeIterator : public LevelDBIterator { + class DataIterator : public LevelDBIterator { public: - static scoped_ptr<TreeIterator> Create(LevelDBTransaction* transaction); - virtual ~TreeIterator(); + static scoped_ptr<DataIterator> Create(LevelDBTransaction* transaction); + virtual ~DataIterator(); virtual bool IsValid() const OVERRIDE; virtual void SeekToLast() OVERRIDE; @@ -94,14 +73,11 @@ class CONTENT_EXPORT LevelDBTransaction virtual base::StringPiece Key() const OVERRIDE; virtual base::StringPiece Value() const OVERRIDE; bool IsDeleted() const; - void Reset(); private: - explicit TreeIterator(LevelDBTransaction* transaction); - mutable TreeType::Iterator iterator_; // Dereferencing this is non-const. - TreeType* tree_; - LevelDBTransaction* transaction_; - std::string key_; + explicit DataIterator(LevelDBTransaction* transaction); + DataType* data_; + DataType::iterator iterator_; }; class TransactionIterator : public LevelDBIterator { @@ -117,20 +93,20 @@ class CONTENT_EXPORT LevelDBTransaction virtual void Prev() OVERRIDE; virtual base::StringPiece Key() const OVERRIDE; virtual base::StringPiece Value() const OVERRIDE; - void TreeChanged(); + void DataChanged(); private: explicit TransactionIterator(scoped_refptr<LevelDBTransaction> transaction); void HandleConflictsAndDeletes(); void SetCurrentIteratorToSmallestKey(); void SetCurrentIteratorToLargestKey(); - void RefreshTreeIterator() const; - bool TreeIteratorIsLower() const; - bool TreeIteratorIsHigher() const; + void RefreshDataIterator() const; + bool DataIteratorIsLower() const; + bool DataIteratorIsHigher() const; scoped_refptr<LevelDBTransaction> transaction_; const LevelDBComparator* comparator_; - mutable scoped_ptr<TreeIterator> tree_iterator_; + mutable scoped_ptr<DataIterator> data_iterator_; scoped_ptr<LevelDBIterator> db_iterator_; LevelDBIterator* current_; @@ -139,19 +115,20 @@ class CONTENT_EXPORT LevelDBTransaction REVERSE }; Direction direction_; - mutable bool tree_changed_; + mutable bool data_changed_; }; void Set(const base::StringPiece& key, std::string* value, bool deleted); - void ClearTree(); + void Clear(); void RegisterIterator(TransactionIterator* iterator); void UnregisterIterator(TransactionIterator* iterator); - void NotifyIteratorsOfTreeChange(); + void NotifyIterators(); LevelDBDatabase* db_; const LevelDBSnapshot snapshot_; const LevelDBComparator* comparator_; - TreeType tree_; + Comparator data_comparator_; + DataType data_; bool finished_; std::set<TransactionIterator*> iterators_; }; diff --git a/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc b/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc index 9ce06641c61..de506c57597 100644 --- a/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc +++ b/chromium/content/browser/indexed_db/leveldb/leveldb_unittest.cc @@ -197,6 +197,52 @@ TEST(LevelDBDatabaseTest, TransactionIterator) { EXPECT_FALSE(it->IsValid()); } +TEST(LevelDBDatabaseTest, TransactionCommitTest) { + base::ScopedTempDir temp_directory; + ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); + + const std::string key1("key1"); + const std::string key2("key2"); + const std::string value1("value1"); + const std::string value2("value2"); + const std::string value3("value3"); + + std::string put_value; + std::string got_value; + SimpleComparator comparator; + bool success; + bool found; + + scoped_ptr<LevelDBDatabase> leveldb; + LevelDBDatabase::Open(temp_directory.path(), &comparator, &leveldb); + EXPECT_TRUE(leveldb); + + scoped_refptr<LevelDBTransaction> transaction = + new LevelDBTransaction(leveldb.get()); + + put_value = value1; + transaction->Put(key1, &put_value); + + put_value = value2; + transaction->Put(key2, &put_value); + + put_value = value3; + transaction->Put(key2, &put_value); + + success = transaction->Commit(); + EXPECT_TRUE(success); + + success = leveldb->Get(key1, &got_value, &found); + EXPECT_TRUE(success); + EXPECT_TRUE(found); + EXPECT_EQ(value1, got_value); + + success = leveldb->Get(key2, &got_value, &found); + EXPECT_TRUE(success); + EXPECT_TRUE(found); + EXPECT_EQ(value3, got_value); +} + } // namespace } // namespace content diff --git a/chromium/content/browser/indexed_db/list_set.h b/chromium/content/browser/indexed_db/list_set.h index 1884b4bb925..e7945cae939 100644 --- a/chromium/content/browser/indexed_db/list_set.h +++ b/chromium/content/browser/indexed_db/list_set.h @@ -47,7 +47,7 @@ class list_set { list_.erase(it); } - bool has(const T& elem) { return set_.find(elem) != set_.end(); } + bool has(const T& elem) const { return set_.find(elem) != set_.end(); } size_t size() const { DCHECK_EQ(list_.size(), set_.size()); diff --git a/chromium/content/browser/loader/async_resource_handler.cc b/chromium/content/browser/loader/async_resource_handler.cc index 02e5552b901..c7d7f49db93 100644 --- a/chromium/content/browser/loader/async_resource_handler.cc +++ b/chromium/content/browser/loader/async_resource_handler.cc @@ -78,12 +78,12 @@ class DependentIOBuffer : public net::WrappedIOBuffer { AsyncResourceHandler::AsyncResourceHandler( ResourceMessageFilter* filter, - int routing_id, + ResourceContext* resource_context, net::URLRequest* request, ResourceDispatcherHostImpl* rdh) : ResourceMessageDelegate(request), filter_(filter), - routing_id_(routing_id), + resource_context_(resource_context), request_(request), rdh_(rdh), pending_data_count_(0), @@ -139,8 +139,8 @@ void AsyncResourceHandler::OnDataReceivedACK(int request_id) { bool AsyncResourceHandler::OnUploadProgress(int request_id, uint64 position, uint64 size) { - return filter_->Send(new ResourceMsg_UploadProgress(routing_id_, request_id, - position, size)); + return filter_->Send(new ResourceMsg_UploadProgress(request_id, position, + size)); } bool AsyncResourceHandler::OnRequestRedirected(int request_id, @@ -150,16 +150,15 @@ bool AsyncResourceHandler::OnRequestRedirected(int request_id, *defer = did_defer_ = true; if (rdh_->delegate()) { - rdh_->delegate()->OnRequestRedirected(new_url, request_, - filter_->resource_context(), - response); + rdh_->delegate()->OnRequestRedirected( + new_url, request_, resource_context_, response); } DevToolsNetLogObserver::PopulateResponseInfo(request_, response); response->head.request_start = request_->creation_time(); response->head.response_start = TimeTicks::Now(); return filter_->Send(new ResourceMsg_ReceivedRedirect( - routing_id_, request_id, new_url, response->head)); + request_id, new_url, response->head)); } bool AsyncResourceHandler::OnResponseStarted(int request_id, @@ -171,16 +170,15 @@ bool AsyncResourceHandler::OnResponseStarted(int request_id, // request commits, avoiding the possibility of e.g. zooming the old content // or of having to layout the new content twice. - ResourceContext* resource_context = filter_->resource_context(); if (rdh_->delegate()) { rdh_->delegate()->OnResponseStarted( - request_, resource_context, response, filter_.get()); + request_, resource_context_, response, filter_.get()); } DevToolsNetLogObserver::PopulateResponseInfo(request_, response); HostZoomMap* host_zoom_map = - GetHostZoomMapForResourceContext(resource_context); + GetHostZoomMapForResourceContext(resource_context_); const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) { @@ -194,16 +192,14 @@ bool AsyncResourceHandler::OnResponseStarted(int request_id, response->head.request_start = request_->creation_time(); response->head.response_start = TimeTicks::Now(); - filter_->Send(new ResourceMsg_ReceivedResponse( - routing_id_, request_id, response->head)); + filter_->Send(new ResourceMsg_ReceivedResponse(request_id, response->head)); sent_received_response_msg_ = true; if (request_->response_info().metadata.get()) { std::vector<char> copy(request_->response_info().metadata->data(), request_->response_info().metadata->data() + request_->response_info().metadata->size()); - filter_->Send(new ResourceMsg_ReceivedCachedMetadata( - routing_id_, request_id, copy)); + filter_->Send(new ResourceMsg_ReceivedCachedMetadata(request_id, copy)); } return true; @@ -254,9 +250,8 @@ bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read, int size; if (!buffer_->ShareToProcess(filter_->PeerHandle(), &handle, &size)) return false; - filter_->Send( - new ResourceMsg_SetDataBuffer(routing_id_, request_id, handle, size, - filter_->peer_pid())); + filter_->Send(new ResourceMsg_SetDataBuffer( + request_id, handle, size, filter_->peer_pid())); sent_first_data_msg_ = true; } @@ -264,9 +259,8 @@ bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read, int encoded_data_length = DevToolsNetLogObserver::GetAndResetEncodedDataLength(request_); - filter_->Send( - new ResourceMsg_DataReceived(routing_id_, request_id, data_offset, - bytes_read, encoded_data_length)); + filter_->Send(new ResourceMsg_DataReceived( + request_id, data_offset, bytes_read, encoded_data_length)); ++pending_data_count_; UMA_HISTOGRAM_CUSTOM_COUNTS( "Net.AsyncResourceHandler_PendingDataCount", @@ -284,8 +278,11 @@ bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read, void AsyncResourceHandler::OnDataDownloaded( int request_id, int bytes_downloaded) { + int encoded_data_length = + DevToolsNetLogObserver::GetAndResetEncodedDataLength(request_); + filter_->Send(new ResourceMsg_DataDownloaded( - routing_id_, request_id, bytes_downloaded)); + request_id, bytes_downloaded, encoded_data_length)); } bool AsyncResourceHandler::OnResponseCompleted( @@ -328,8 +325,7 @@ bool AsyncResourceHandler::OnResponseCompleted( error_code = net::ERR_FAILED; } - filter_->Send(new ResourceMsg_RequestComplete(routing_id_, - request_id, + filter_->Send(new ResourceMsg_RequestComplete(request_id, error_code, was_ignored_by_handler, security_info, diff --git a/chromium/content/browser/loader/async_resource_handler.h b/chromium/content/browser/loader/async_resource_handler.h index 5f36112a9bc..b43e0ef3745 100644 --- a/chromium/content/browser/loader/async_resource_handler.h +++ b/chromium/content/browser/loader/async_resource_handler.h @@ -18,6 +18,7 @@ class URLRequest; namespace content { class ResourceBuffer; +class ResourceContext; class ResourceDispatcherHostImpl; class ResourceMessageFilter; class SharedIOBuffer; @@ -28,7 +29,7 @@ class AsyncResourceHandler : public ResourceHandler, public ResourceMessageDelegate { public: AsyncResourceHandler(ResourceMessageFilter* filter, - int routing_id, + ResourceContext* resource_context, net::URLRequest* request, ResourceDispatcherHostImpl* rdh); virtual ~AsyncResourceHandler(); @@ -75,7 +76,7 @@ class AsyncResourceHandler : public ResourceHandler, scoped_refptr<ResourceBuffer> buffer_; scoped_refptr<ResourceMessageFilter> filter_; - int routing_id_; + ResourceContext* resource_context_; net::URLRequest* request_; ResourceDispatcherHostImpl* rdh_; diff --git a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc index cb8896f2e05..0a763fede6c 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc @@ -15,9 +15,9 @@ #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" -#include "content/shell/shell_content_browser_client.h" -#include "content/shell/shell_network_delegate.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/browser/shell_network_delegate.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "content/test/net/url_request_failed_job.h" diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc index c37137dad89..382a68124b1 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc @@ -58,7 +58,6 @@ #include "content/public/browser/download_manager.h" #include "content/public/browser/download_url_parameters.h" #include "content/public/browser/global_request_id.h" -#include "content/public/browser/notification_service.h" #include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/browser/resource_request_details.h" #include "content/public/browser/resource_throttle.h" @@ -78,16 +77,17 @@ #include "net/base/upload_data_stream.h" #include "net/cert/cert_status_flags.h" #include "net/cookies/cookie_monster.h" -#include "net/http/http_cache.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" -#include "net/http/http_transaction_factory.h" #include "net/ssl/ssl_cert_request_info.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_job_factory.h" #include "webkit/browser/appcache/appcache_interceptor.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/common/blob/blob_data.h" +#include "webkit/browser/blob/blob_data_handle.h" +#include "webkit/browser/blob/blob_storage_context.h" +#include "webkit/browser/blob/blob_url_request_job_factory.h" #include "webkit/browser/fileapi/file_permission_policy.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/common/appcache/appcache_interfaces.h" @@ -139,7 +139,6 @@ const int kAllNetErrorCodes[] = { // Aborts a request before an URLRequest has actually been created. void AbortRequestBeforeItStarts(ResourceMessageFilter* filter, IPC::Message* sync_result, - int route_id, int request_id) { if (sync_result) { SyncLoadResult result; @@ -149,7 +148,6 @@ void AbortRequestBeforeItStarts(ResourceMessageFilter* filter, } else { // Tell the renderer that this request was disallowed. filter->Send(new ResourceMsg_RequestComplete( - route_id, request_id, net::ERR_ABORTED, false, @@ -216,11 +214,11 @@ bool ShouldServiceRequest(int process_type, return false; } if (iter->type() == ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM) { - fileapi::FileSystemURL url = file_system_context->CrackURL(iter->url()); - if (!policy->HasPermissionsForFileSystemFile( - child_id, url, fileapi::kReadFilePermissions)) { + fileapi::FileSystemURL url = + file_system_context->CrackURL(iter->filesystem_url()); + if (!policy->CanReadFileSystemFile(child_id, url)) { NOTREACHED() << "Denied unauthorized upload of " - << iter->url().spec(); + << iter->filesystem_url().spec(); return false; } } @@ -266,17 +264,28 @@ int GetCertID(net::URLRequest* request, int child_id) { return 0; } -template <class T> -void NotifyOnUI(int type, int render_process_id, int render_view_id, - scoped_ptr<T> detail) { +void NotifyRedirectOnUI(int render_process_id, + int render_view_id, + scoped_ptr<ResourceRedirectDetails> details) { RenderViewHostImpl* host = RenderViewHostImpl::FromID(render_process_id, render_view_id); - if (host) { - RenderViewHostDelegate* delegate = host->GetDelegate(); - NotificationService::current()->Notify( - type, Source<WebContents>(delegate->GetAsWebContents()), - Details<T>(detail.get())); - } + if (!host) + return; + + RenderViewHostDelegate* delegate = host->GetDelegate(); + delegate->DidGetRedirectForResourceRequest(*details.get()); +} + +void NotifyResponseOnUI(int render_process_id, + int render_view_id, + scoped_ptr<ResourceRequestDetails> details) { + RenderViewHostImpl* host = + RenderViewHostImpl::FromID(render_process_id, render_view_id); + if (!host) + return; + + RenderViewHostDelegate* delegate = host->GetDelegate(); + delegate->DidGetResourceResponseStart(*details.get()); } } // namespace @@ -502,6 +511,14 @@ net::Error ResourceDispatcherHostImpl::BeginDownload( CreateRequestInfo(child_id, route_id, true, context); extra_info->AssociateWithRequest(request.get()); // Request takes ownership. + if (request->url().SchemeIs(chrome::kBlobScheme)) { + ChromeBlobStorageContext* blob_context = + GetChromeBlobStorageContextForResourceContext(context); + webkit_blob::BlobProtocolHandler::SetRequestedBlobDataHandle( + request.get(), + blob_context->context()->GetBlobDataFromPublicURL(request->url())); + } + // From this point forward, the |DownloadResourceHandler| is responsible for // |started_callback|. scoped_ptr<ResourceHandler> handler( @@ -681,8 +698,7 @@ void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader, BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( - &NotifyOnUI<ResourceRedirectDetails>, - static_cast<int>(NOTIFICATION_RESOURCE_RECEIVED_REDIRECT), + &NotifyRedirectOnUI, render_process_id, render_view_id, base::Passed(&detail))); } @@ -713,8 +729,7 @@ void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( - &NotifyOnUI<ResourceRequestDetails>, - static_cast<int>(NOTIFICATION_RESOURCE_RESPONSE_STARTED), + &NotifyResponseOnUI, render_process_id, render_view_id, base::Passed(&detail))); } @@ -817,8 +832,6 @@ bool ResourceDispatcherHostImpl::OnMessageReceived( IPC_MESSAGE_HANDLER(ResourceHostMsg_DataDownloaded_ACK, OnDataDownloadedACK) IPC_MESSAGE_HANDLER(ResourceHostMsg_UploadProgress_ACK, OnUploadProgressACK) IPC_MESSAGE_HANDLER(ResourceHostMsg_CancelRequest, OnCancelRequest) - IPC_MESSAGE_HANDLER(ViewHostMsg_DidLoadResourceFromMemoryCache, - OnDidLoadResourceFromMemoryCache) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP_EX() @@ -838,12 +851,6 @@ bool ResourceDispatcherHostImpl::OnMessageReceived( } } - if (message.type() == ViewHostMsg_DidLoadResourceFromMemoryCache::ID) { - // We just needed to peek at this message. We still want it to reach its - // normal destination. - handled = false; - } - filter_ = NULL; return handled; } @@ -914,14 +921,16 @@ void ResourceDispatcherHostImpl::BeginRequest( } } - ResourceContext* resource_context = filter_->resource_context(); + ResourceContext* resource_context = NULL; + net::URLRequestContext* request_context = NULL; + filter_->GetContexts(request_data, &resource_context, &request_context); // http://crbug.com/90971 CHECK(ContainsKey(active_resource_contexts_, resource_context)); if (is_shutdown_ || !ShouldServiceRequest(process_type, child_id, request_data, filter_->file_system_context())) { - AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id); + AbortRequestBeforeItStarts(filter_, sync_result, request_id); return; } @@ -934,7 +943,7 @@ void ResourceDispatcherHostImpl::BeginRequest( request_data.url, request_data.resource_type, resource_context)) { - AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id); + AbortRequestBeforeItStarts(filter_, sync_result, request_id); return; } @@ -958,9 +967,7 @@ void ResourceDispatcherHostImpl::BeginRequest( // chance to reset some state before we complete the transfer. deferred_loader->WillCompleteTransfer(); } else { - net::URLRequestContext* context = - filter_->GetURLRequestContext(request_data.resource_type); - new_request.reset(context->CreateRequest(request_data.url, NULL)); + new_request.reset(request_context->CreateRequest(request_data.url, NULL)); request = new_request.get(); request->set_method(request_data.method); @@ -980,9 +987,12 @@ void ResourceDispatcherHostImpl::BeginRequest( // Resolve elements from request_body and prepare upload data. if (request_data.request_body.get()) { + webkit_blob::BlobStorageContext* blob_context = NULL; + if (filter_->blob_storage_context()) + blob_context = filter_->blob_storage_context()->context(); request->set_upload(UploadDataStreamBuilder::Build( request_data.request_body.get(), - filter_->blob_storage_context()->controller(), + blob_context, filter_->file_system_context(), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE) .get())); @@ -1017,9 +1027,10 @@ void ResourceDispatcherHostImpl::BeginRequest( if (request->url().SchemeIs(chrome::kBlobScheme)) { // Hang on to a reference to ensure the blob is not released prior // to the job being started. - extra_info->set_requested_blob_data( - filter_->blob_storage_context()->controller()-> - GetBlobDataFromUrl(request->url())); + webkit_blob::BlobProtocolHandler::SetRequestedBlobDataHandle( + request, + filter_->blob_storage_context()->context()-> + GetBlobDataFromPublicURL(request->url())); } // Have the appcache associate its extra info with the request. @@ -1031,10 +1042,10 @@ void ResourceDispatcherHostImpl::BeginRequest( scoped_ptr<ResourceHandler> handler; if (sync_result) { handler.reset(new SyncResourceHandler( - filter_, request, sync_result, this)); + filter_, resource_context, request, sync_result, this)); } else { handler.reset(new AsyncResourceHandler( - filter_, route_id, request, this)); + filter_, resource_context, request, this)); } // The RedirectToFileResourceHandler depends on being next in the chain. @@ -1184,20 +1195,6 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( true); // is_async } - -void ResourceDispatcherHostImpl::OnDidLoadResourceFromMemoryCache( - const GURL& url, - const std::string& security_info, - const std::string& http_method, - const std::string& mime_type, - ResourceType::Type resource_type) { - if (!url.is_valid() || !(url.SchemeIs("http") || url.SchemeIs("https"))) - return; - - filter_->GetURLRequestContext(resource_type)->http_transaction_factory()-> - GetCache()->OnExternalCacheHit(url, http_method); -} - void ResourceDispatcherHostImpl::OnRenderViewHostCreated( int child_id, int route_id) { @@ -1813,7 +1810,7 @@ void ResourceDispatcherHostImpl::UnregisterResourceMessageDelegate( DelegateMap::iterator it = delegate_map_.find(id); DCHECK(it->second->HasObserver(delegate)); it->second->RemoveObserver(delegate); - if (it->second->size() == 0) { + if (!it->second->might_have_observers()) { delete it->second; delegate_map_.erase(it); } @@ -1850,7 +1847,7 @@ int ResourceDispatcherHostImpl::BuildLoadFlagsForRequest( HttpAuthRelationType relation_type = HttpAuthRelationTypeOf( request_data.url, request_data.first_party_for_cookies); if (relation_type == HTTP_AUTH_RELATION_BLOCKED_CROSS) { - load_flags |= (net::LOAD_DO_NOT_SEND_AUTH_DATA | + load_flags |= (net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY | net::LOAD_DO_NOT_PROMPT_FOR_LOGIN); } } diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.h b/chromium/content/browser/loader/resource_dispatcher_host_impl.h index af53b70ce75..e6f2241d9a0 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.h +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.h @@ -162,13 +162,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl return save_file_manager_.get(); } - // Called when the renderer loads a resource from its internal cache. - void OnDidLoadResourceFromMemoryCache(const GURL& url, - const std::string& security_info, - const std::string& http_method, - const std::string& mime_type, - ResourceType::Type resource_type); - // Called when a RenderViewHost is created. void OnRenderViewHostCreated(int child_id, int route_id); diff --git a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc index be463398f8d..89b17c44c73 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc @@ -159,22 +159,6 @@ void ResourceIPCAccumulator::GetClassifiedMessages(ClassifiedMessages* msgs) { } } -class MockURLRequestContextSelector - : public ResourceMessageFilter::URLRequestContextSelector { - public: - explicit MockURLRequestContextSelector( - net::URLRequestContext* request_context) - : request_context_(request_context) {} - - virtual net::URLRequestContext* GetRequestContext( - ResourceType::Type request_type) OVERRIDE { - return request_context_; - } - - private: - net::URLRequestContext* const request_context_; -}; - // This class forwards the incoming messages to the ResourceDispatcherHostTest. // This is used to emulate different sub-processes, since this filter will // have a different ID than the original. For the test, we want all the incoming @@ -185,11 +169,11 @@ class ForwardingFilter : public ResourceMessageFilter { ResourceContext* resource_context) : ResourceMessageFilter( ChildProcessHostImpl::GenerateChildProcessUniqueId(), - PROCESS_TYPE_RENDERER, - resource_context, NULL, NULL, NULL, - new MockURLRequestContextSelector( - resource_context->GetRequestContext())), - dest_(dest) { + PROCESS_TYPE_RENDERER, NULL, NULL, NULL, + base::Bind(&ForwardingFilter::GetContexts, + base::Unretained(this))), + dest_(dest), + resource_context_(resource_context) { OnChannelConnected(base::GetCurrentProcId()); } @@ -200,11 +184,21 @@ class ForwardingFilter : public ResourceMessageFilter { return dest_->Send(msg); } + ResourceContext* resource_context() { return resource_context_; } + protected: virtual ~ForwardingFilter() {} private: + void GetContexts(const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context) { + *resource_context = resource_context_; + *request_context = resource_context_->GetRequestContext(); + } + IPC::Sender* dest_; + ResourceContext* resource_context_; DISALLOW_COPY_AND_ASSIGN(ForwardingFilter); }; @@ -708,7 +702,7 @@ class ResourceDispatcherHostTest : public testing::Test, bool result = PickleIterator(msg).ReadInt(&request_id); DCHECK(result); scoped_ptr<IPC::Message> ack( - new ResourceHostMsg_DataReceived_ACK(msg.routing_id(), request_id)); + new ResourceHostMsg_DataReceived_ACK(request_id)); base::MessageLoop::current()->PostTask( FROM_HERE, @@ -1609,7 +1603,7 @@ TEST_F(ResourceDispatcherHostTest, IgnoreCancelForDownloads) { EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage()); // And now simulate a cancellation coming from the renderer. - ResourceHostMsg_CancelRequest msg(filter_->child_id(), request_id); + ResourceHostMsg_CancelRequest msg(request_id); bool msg_was_ok; host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok); @@ -1644,7 +1638,7 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContext) { EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage()); // And now simulate a cancellation coming from the renderer. - ResourceHostMsg_CancelRequest msg(filter_->child_id(), request_id); + ResourceHostMsg_CancelRequest msg(request_id); bool msg_was_ok; host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok); @@ -1685,7 +1679,7 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) { GURL("http://example.com/blah")); // And now simulate a cancellation coming from the renderer. - ResourceHostMsg_CancelRequest msg(filter_->child_id(), request_id); + ResourceHostMsg_CancelRequest msg(request_id); bool msg_was_ok; host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok); @@ -1832,7 +1826,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationAndThenRedirect) { // Now, simulate the renderer choosing to follow the redirect. ResourceHostMsg_FollowRedirect redirect_msg( - new_render_view_id, new_request_id, false, GURL()); + new_request_id, false, GURL()); host_.OnMessageReceived(redirect_msg, second_filter.get(), &msg_was_ok); base::MessageLoop::current()->RunUntilIdle(); @@ -1938,7 +1932,7 @@ TEST_F(ResourceDispatcherHostTest, DelayedDataReceivedACKs) { EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type()); - ResourceHostMsg_DataReceived_ACK msg(0, 1); + ResourceHostMsg_DataReceived_ACK msg(1); bool msg_was_ok; host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok); } @@ -1973,7 +1967,7 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) { // Send some unexpected ACKs. for (size_t i = 0; i < 128; ++i) { - ResourceHostMsg_DataReceived_ACK msg(0, 1); + ResourceHostMsg_DataReceived_ACK msg(1); bool msg_was_ok; host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok); } @@ -1992,7 +1986,7 @@ TEST_F(ResourceDispatcherHostTest, DataReceivedUnexpectedACKs) { EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type()); - ResourceHostMsg_DataReceived_ACK msg(0, 1); + ResourceHostMsg_DataReceived_ACK msg(1); bool msg_was_ok; host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok); } diff --git a/chromium/content/browser/loader/resource_message_filter.cc b/chromium/content/browser/loader/resource_message_filter.cc index 7c523975fba..26ba80fbaa4 100644 --- a/chromium/content/browser/loader/resource_message_filter.cc +++ b/chromium/content/browser/loader/resource_message_filter.cc @@ -15,21 +15,16 @@ namespace content { ResourceMessageFilter::ResourceMessageFilter( int child_id, int process_type, - ResourceContext* resource_context, ChromeAppCacheService* appcache_service, ChromeBlobStorageContext* blob_storage_context, fileapi::FileSystemContext* file_system_context, - URLRequestContextSelector* url_request_context_selector) + const GetContextsCallback& get_contexts_callback) : child_id_(child_id), process_type_(process_type), - resource_context_(resource_context), appcache_service_(appcache_service), blob_storage_context_(blob_storage_context), file_system_context_(file_system_context), - url_request_context_selector_(url_request_context_selector) { - DCHECK(resource_context); - DCHECK(url_request_context_selector); - // |appcache_service| and |blob_storage_context| may be NULL in unittests. + get_contexts_callback_(get_contexts_callback) { } ResourceMessageFilter::~ResourceMessageFilter() { @@ -49,9 +44,11 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message, message, this, message_was_ok); } -net::URLRequestContext* ResourceMessageFilter::GetURLRequestContext( - ResourceType::Type type) { - return url_request_context_selector_->GetRequestContext(type); +void ResourceMessageFilter::GetContexts( + const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context) { + return get_contexts_callback_.Run(request, resource_context, request_context); } } // namespace content diff --git a/chromium/content/browser/loader/resource_message_filter.h b/chromium/content/browser/loader/resource_message_filter.h index 2c56af0dd6d..925e4525e28 100644 --- a/chromium/content/browser/loader/resource_message_filter.h +++ b/chromium/content/browser/loader/resource_message_filter.h @@ -5,11 +5,14 @@ #ifndef CONTENT_BROWSER_LOADER_RESOURCE_MESSAGE_FILTER_H_ #define CONTENT_BROWSER_LOADER_RESOURCE_MESSAGE_FILTER_H_ +#include "base/callback_forward.h" #include "base/memory/scoped_ptr.h" #include "content/common/content_export.h" #include "content/public/browser/browser_message_filter.h" #include "webkit/common/resource_type.h" +struct ResourceHostMsg_Request; + namespace fileapi { class FileSystemContext; } // namespace fileapi @@ -31,36 +34,32 @@ class ResourceContext; // will not interfere with browser UI. class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { public: - // Allows selecting the net::URLRequestContext used to service requests. - class URLRequestContextSelector { - public: - URLRequestContextSelector() {} - virtual ~URLRequestContextSelector() {} - - virtual net::URLRequestContext* GetRequestContext( - ResourceType::Type request_type) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(URLRequestContextSelector); - }; + typedef base::Callback<void(const ResourceHostMsg_Request&, + ResourceContext**, + net::URLRequestContext**)> GetContextsCallback; + // |appcache_service|, |blob_storage_context|, |file_system_context| may be + // NULL in unittests or for requests from the (NPAPI) plugin process. ResourceMessageFilter( int child_id, int process_type, - ResourceContext* resource_context, ChromeAppCacheService* appcache_service, ChromeBlobStorageContext* blob_storage_context, fileapi::FileSystemContext* file_system_context, - URLRequestContextSelector* url_request_context_selector); + const GetContextsCallback& get_contexts_callback); // BrowserMessageFilter implementation. virtual void OnChannelClosing() OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok) OVERRIDE; - ResourceContext* resource_context() const { - return resource_context_; - } + void GetContexts(const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context); + + // Returns the net::URLRequestContext for the given request. + net::URLRequestContext* GetURLRequestContext( + ResourceType::Type request_type); ChromeAppCacheService* appcache_service() const { return appcache_service_.get(); @@ -74,10 +73,6 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { return file_system_context_.get(); } - // Returns the net::URLRequestContext for the given request. - net::URLRequestContext* GetURLRequestContext( - ResourceType::Type request_type); - int child_id() const { return child_id_; } int process_type() const { return process_type_; } @@ -91,14 +86,11 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { int process_type_; - // Owned by ProfileIOData* which is guaranteed to outlive us. - ResourceContext* resource_context_; - scoped_refptr<ChromeAppCacheService> appcache_service_; scoped_refptr<ChromeBlobStorageContext> blob_storage_context_; scoped_refptr<fileapi::FileSystemContext> file_system_context_; - const scoped_ptr<URLRequestContextSelector> url_request_context_selector_; + GetContextsCallback get_contexts_callback_; DISALLOW_IMPLICIT_CONSTRUCTORS(ResourceMessageFilter); }; diff --git a/chromium/content/browser/loader/resource_request_info_impl.cc b/chromium/content/browser/loader/resource_request_info_impl.cc index 51e39ff3ddd..c41fe39e565 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.cc +++ b/chromium/content/browser/loader/resource_request_info_impl.cc @@ -9,7 +9,6 @@ #include "content/common/net/url_request_user_data.h" #include "content/public/browser/global_request_id.h" #include "net/url_request/url_request.h" -#include "webkit/common/blob/blob_data.h" namespace content { @@ -196,6 +195,9 @@ bool ResourceRequestInfoImpl::GetAssociatedRenderView( *render_view_id = -1; return false; } + } else if (process_type_ == PROCESS_TYPE_PLUGIN) { + *render_process_id = origin_pid_; + *render_view_id = route_id_; } else { *render_process_id = child_id_; *render_view_id = route_id_; @@ -226,9 +228,4 @@ GlobalRoutingID ResourceRequestInfoImpl::GetGlobalRoutingID() const { return GlobalRoutingID(child_id_, route_id_); } -void ResourceRequestInfoImpl::set_requested_blob_data( - webkit_blob::BlobData* data) { - requested_blob_data_ = data; -} - } // namespace content diff --git a/chromium/content/browser/loader/resource_request_info_impl.h b/chromium/content/browser/loader/resource_request_info_impl.h index d99586530ab..e615116c152 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.h +++ b/chromium/content/browser/loader/resource_request_info_impl.h @@ -16,10 +16,6 @@ #include "net/base/load_states.h" #include "webkit/common/resource_type.h" -namespace webkit_blob { -class BlobData; -} - namespace content { class CrossSiteResourceHandler; class ResourceContext; @@ -116,13 +112,6 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, int memory_cost() const { return memory_cost_; } void set_memory_cost(int cost) { memory_cost_ = cost; } - // We hold a reference to the requested blob data to ensure it doesn't - // get finally released prior to the net::URLRequestJob being started. - webkit_blob::BlobData* requested_blob_data() const { - return requested_blob_data_.get(); - } - void set_requested_blob_data(webkit_blob::BlobData* data); - private: // Non-owning, may be NULL. CrossSiteResourceHandler* cross_site_handler_; @@ -144,7 +133,6 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, ResourceType::Type resource_type_; PageTransition transition_type_; int memory_cost_; - scoped_refptr<webkit_blob::BlobData> requested_blob_data_; WebKit::WebReferrerPolicy referrer_policy_; ResourceContext* context_; bool is_async_; diff --git a/chromium/content/browser/loader/resource_scheduler.cc b/chromium/content/browser/loader/resource_scheduler.cc index 7e70c457814..0277783bfdf 100644 --- a/chromium/content/browser/loader/resource_scheduler.cc +++ b/chromium/content/browser/loader/resource_scheduler.cc @@ -81,6 +81,8 @@ class ResourceScheduler::ScheduledResourceRequest ready_(false), deferred_(false), scheduler_(scheduler) { + TRACE_EVENT_ASYNC_BEGIN1("net", "URLRequest", request_, + "url", request->url().spec()); } virtual ~ScheduledResourceRequest() { @@ -88,6 +90,7 @@ class ResourceScheduler::ScheduledResourceRequest } void Start() { + TRACE_EVENT_ASYNC_STEP0("net", "URLRequest", request_, "Queued"); ready_ = true; if (deferred_ && request_->status().is_success()) { deferred_ = false; diff --git a/chromium/content/browser/loader/resource_scheduler_unittest.cc b/chromium/content/browser/loader/resource_scheduler_unittest.cc index 9d87f2df047..2c4c1101962 100644 --- a/chromium/content/browser/loader/resource_scheduler_unittest.cc +++ b/chromium/content/browser/loader/resource_scheduler_unittest.cc @@ -93,30 +93,29 @@ class FakeResourceContext : public ResourceContext { virtual bool AllowCameraAccess(const GURL& origin) OVERRIDE { return false; } }; -class FakeURLRequestContextSelector - : public ResourceMessageFilter::URLRequestContextSelector { - private: - virtual net::URLRequestContext* GetRequestContext( - ResourceType::Type) OVERRIDE { - return NULL; - } -}; - class FakeResourceMessageFilter : public ResourceMessageFilter { public: FakeResourceMessageFilter(int child_id) - : ResourceMessageFilter(child_id, - PROCESS_TYPE_RENDERER, - &context_, - NULL /* appcache_service */, - NULL /* blob_storage_context */, - NULL /* file_system_context */, - new FakeURLRequestContextSelector) { + : ResourceMessageFilter( + child_id, + PROCESS_TYPE_RENDERER, + NULL /* appcache_service */, + NULL /* blob_storage_context */, + NULL /* file_system_context */, + base::Bind(&FakeResourceMessageFilter::GetContexts, + base::Unretained(this))) { } private: virtual ~FakeResourceMessageFilter() {} + void GetContexts(const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context) { + *resource_context = &context_; + *request_context = NULL; + } + FakeResourceContext context_; }; @@ -193,8 +192,7 @@ class ResourceSchedulerTest : public testing::Test { const ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest( request->url_request()); const GlobalRequestID& id = info->GetGlobalRequestID(); - ResourceHostMsg_DidChangePriority msg( - kRouteId, id.request_id, new_priority); + ResourceHostMsg_DidChangePriority msg(id.request_id, new_priority); bool ok = false; rdh_.OnMessageReceived(msg, filter.get(), &ok); EXPECT_TRUE(ok); diff --git a/chromium/content/browser/loader/sync_resource_handler.cc b/chromium/content/browser/loader/sync_resource_handler.cc index 1db8dca79b0..b1a71622c2b 100644 --- a/chromium/content/browser/loader/sync_resource_handler.cc +++ b/chromium/content/browser/loader/sync_resource_handler.cc @@ -18,11 +18,13 @@ namespace content { SyncResourceHandler::SyncResourceHandler( ResourceMessageFilter* filter, + ResourceContext* resource_context, net::URLRequest* request, IPC::Message* result_message, ResourceDispatcherHostImpl* resource_dispatcher_host) : read_buffer_(new net::IOBuffer(kReadBufSize)), filter_(filter), + resource_context_(resource_context), request_(request), result_message_(result_message), rdh_(resource_dispatcher_host) { @@ -48,9 +50,8 @@ bool SyncResourceHandler::OnRequestRedirected( ResourceResponse* response, bool* defer) { if (rdh_->delegate()) { - rdh_->delegate()->OnRequestRedirected(new_url, request_, - filter_->resource_context(), - response); + rdh_->delegate()->OnRequestRedirected( + new_url, request_, resource_context_, response); } DevToolsNetLogObserver::PopulateResponseInfo(request_, response); @@ -71,7 +72,7 @@ bool SyncResourceHandler::OnResponseStarted( bool* defer) { if (rdh_->delegate()) { rdh_->delegate()->OnResponseStarted( - request_, filter_->resource_context(), response, filter_.get()); + request_, resource_context_, response, filter_.get()); } DevToolsNetLogObserver::PopulateResponseInfo(request_, response); diff --git a/chromium/content/browser/loader/sync_resource_handler.h b/chromium/content/browser/loader/sync_resource_handler.h index af254729332..838a05a225c 100644 --- a/chromium/content/browser/loader/sync_resource_handler.h +++ b/chromium/content/browser/loader/sync_resource_handler.h @@ -20,6 +20,7 @@ class URLRequest; } namespace content { +class ResourceContext; class ResourceDispatcherHostImpl; class ResourceMessageFilter; @@ -28,6 +29,7 @@ class ResourceMessageFilter; class SyncResourceHandler : public ResourceHandler { public: SyncResourceHandler(ResourceMessageFilter* filter, + ResourceContext* resource_context, net::URLRequest* request, IPC::Message* result_message, ResourceDispatcherHostImpl* resource_dispatcher_host); @@ -65,6 +67,7 @@ class SyncResourceHandler : public ResourceHandler { SyncLoadResult result_; scoped_refptr<ResourceMessageFilter> filter_; + ResourceContext* resource_context_; net::URLRequest* request_; IPC::Message* result_message_; ResourceDispatcherHostImpl* rdh_; diff --git a/chromium/content/browser/loader/upload_data_stream_builder.cc b/chromium/content/browser/loader/upload_data_stream_builder.cc index 1a7dfc33e09..c22a8a8d444 100644 --- a/chromium/content/browser/loader/upload_data_stream_builder.cc +++ b/chromium/content/browser/loader/upload_data_stream_builder.cc @@ -8,12 +8,14 @@ #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_data_stream.h" #include "net/base/upload_file_element_reader.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_data_handle.h" +#include "webkit/browser/blob/blob_storage_context.h" #include "webkit/browser/fileapi/upload_file_system_file_element_reader.h" #include "webkit/common/resource_request_body.h" using webkit_blob::BlobData; -using webkit_blob::BlobStorageController; +using webkit_blob::BlobDataHandle; +using webkit_blob::BlobStorageContext; using webkit_glue::ResourceRequestBody; namespace content { @@ -64,49 +66,51 @@ class FileElementReader : public net::UploadFileElementReader { void ResolveBlobReference( ResourceRequestBody* body, - webkit_blob::BlobStorageController* blob_controller, - const GURL& blob_url, + webkit_blob::BlobStorageContext* blob_context, + const ResourceRequestBody::Element& element, std::vector<const ResourceRequestBody::Element*>* resolved_elements) { - DCHECK(blob_controller); - BlobData* blob_data = blob_controller->GetBlobDataFromUrl(blob_url); - DCHECK(blob_data); - if (!blob_data) + DCHECK(blob_context); + std::string uuid = element.blob_uuid(); + if (uuid.empty()) + uuid = blob_context->LookupUuidFromDeprecatedURL(element.blob_url()); + scoped_ptr<webkit_blob::BlobDataHandle> handle = + blob_context->GetBlobDataFromUUID(uuid); + DCHECK(handle); + if (!handle) return; // If there is no element in the referred blob data, just return. - if (blob_data->items().empty()) + if (handle->data()->items().empty()) return; - // Ensure the blob and any attached shareable files survive until - // upload completion. - body->SetUserData(blob_data, new base::UserDataAdapter<BlobData>(blob_data)); - - // Append the elements in the referred blob data. - for (size_t i = 0; i < blob_data->items().size(); ++i) { - const BlobData::Item& item = blob_data->items().at(i); + // Append the elements in the referenced blob data. + for (size_t i = 0; i < handle->data()->items().size(); ++i) { + const BlobData::Item& item = handle->data()->items().at(i); DCHECK_NE(BlobData::Item::TYPE_BLOB, item.type()); resolved_elements->push_back(&item); } + + // Ensure the blob and any attached shareable files survive until + // upload completion. The |body| takes ownership of |handle|. + const void* key = handle.get(); + body->SetUserData(key, handle.release()); } } // namespace scoped_ptr<net::UploadDataStream> UploadDataStreamBuilder::Build( ResourceRequestBody* body, - BlobStorageController* blob_controller, + BlobStorageContext* blob_context, fileapi::FileSystemContext* file_system_context, base::TaskRunner* file_task_runner) { // Resolve all blob elements. std::vector<const ResourceRequestBody::Element*> resolved_elements; for (size_t i = 0; i < body->elements()->size(); ++i) { const ResourceRequestBody::Element& element = (*body->elements())[i]; - if (element.type() == ResourceRequestBody::Element::TYPE_BLOB) { - ResolveBlobReference(body, blob_controller, element.url(), - &resolved_elements); - } else { - // No need to resolve, just append the element. + if (element.type() == ResourceRequestBody::Element::TYPE_BLOB) + ResolveBlobReference(body, blob_context, element, &resolved_elements); + else resolved_elements.push_back(&element); - } } ScopedVector<net::UploadElementReader> element_readers; @@ -124,7 +128,7 @@ scoped_ptr<net::UploadDataStream> UploadDataStreamBuilder::Build( element_readers.push_back( new fileapi::UploadFileSystemFileElementReader( file_system_context, - element.url(), + element.filesystem_url(), element.offset(), element.length(), element.expected_modification_time())); diff --git a/chromium/content/browser/loader/upload_data_stream_builder.h b/chromium/content/browser/loader/upload_data_stream_builder.h index b5115466565..e2b6ce3fc29 100644 --- a/chromium/content/browser/loader/upload_data_stream_builder.h +++ b/chromium/content/browser/loader/upload_data_stream_builder.h @@ -21,7 +21,7 @@ class UploadDataStream; } namespace webkit_blob { -class BlobStorageController; +class BlobStorageContext; } namespace webkit_glue { @@ -34,7 +34,7 @@ class CONTENT_EXPORT UploadDataStreamBuilder { public: // Creates a new UploadDataStream from this request body. // - // This also resolves any blob references using the given |blob_controller| + // This also resolves any blob references using the given |blob_context| // and binds those blob references to the ResourceRequestBody ensuring that // the blob data remains valid for the lifetime of the ResourceRequestBody // object. @@ -44,7 +44,7 @@ class CONTENT_EXPORT UploadDataStreamBuilder { // when the data gets uploaded. static scoped_ptr<net::UploadDataStream> Build( webkit_glue::ResourceRequestBody* body, - webkit_blob::BlobStorageController* blob_controller, + webkit_blob::BlobStorageContext* blob_context, fileapi::FileSystemContext* file_system_context, base::TaskRunner* file_task_runner); }; diff --git a/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc b/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc index 9972418a06d..4c9661cbee6 100644 --- a/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc +++ b/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc @@ -16,11 +16,12 @@ #include "net/base/upload_file_element_reader.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" -#include "webkit/browser/blob/blob_storage_controller.h" +#include "webkit/browser/blob/blob_storage_context.h" #include "webkit/common/resource_request_body.h" using webkit_blob::BlobData; -using webkit_blob::BlobStorageController; +using webkit_blob::BlobDataHandle; +using webkit_blob::BlobStorageContext; using webkit_glue::ResourceRequestBody; namespace content { @@ -98,24 +99,20 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1); base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2); - BlobStorageController blob_storage_controller; - scoped_refptr<BlobData> blob_data(new BlobData()); + BlobStorageContext blob_storage_context; - GURL blob_url0("blob://url_0"); - blob_storage_controller.AddFinishedBlob(blob_url0, blob_data.get()); + const std::string blob_id0("id-0"); + scoped_refptr<BlobData> blob_data(new BlobData(blob_id0)); + scoped_ptr<BlobDataHandle> handle1 = + blob_storage_context.AddFinishedBlob(blob_data); + const std::string blob_id1("id-1"); + blob_data = new BlobData(blob_id1); blob_data->AppendData("BlobData"); blob_data->AppendFile( base::FilePath(FILE_PATH_LITERAL("BlobFile.txt")), 0, 20, time1); - - GURL blob_url1("blob://url_1"); - blob_storage_controller.AddFinishedBlob(blob_url1, blob_data.get()); - - GURL blob_url2("blob://url_2"); - blob_storage_controller.CloneBlob(blob_url2, blob_url1); - - GURL blob_url3("blob://url_3"); - blob_storage_controller.CloneBlob(blob_url3, blob_url2); + scoped_ptr<BlobDataHandle> handle2 = + blob_storage_context.AddFinishedBlob(blob_data); // Setup upload data elements for comparison. ResourceRequestBody::Element blob_element1, blob_element2; @@ -144,7 +141,7 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { scoped_ptr<net::UploadDataStream> upload( UploadDataStreamBuilder::Build(request_body.get(), - &blob_storage_controller, + &blob_storage_context, NULL, base::MessageLoopProxy::current().get())); @@ -154,22 +151,22 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { // Test having only one blob reference that refers to empty blob data. request_body = new ResourceRequestBody(); - request_body->AppendBlob(blob_url0); + request_body->AppendBlob(blob_id0); upload = UploadDataStreamBuilder::Build(request_body.get(), - &blob_storage_controller, + &blob_storage_context, NULL, base::MessageLoopProxy::current().get()); ASSERT_EQ(0U, upload->element_readers().size()); // Test having only one blob reference. request_body = new ResourceRequestBody(); - request_body->AppendBlob(blob_url1); + request_body->AppendBlob(blob_id1); upload = UploadDataStreamBuilder::Build(request_body.get(), - &blob_storage_controller, + &blob_storage_context, NULL, base::MessageLoopProxy::current().get()); ASSERT_EQ(2U, upload->element_readers().size()); @@ -178,7 +175,7 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { // Test having one blob reference at the beginning. request_body = new ResourceRequestBody(); - request_body->AppendBlob(blob_url1); + request_body->AppendBlob(blob_id1); request_body->AppendBytes(upload_element1.bytes(), upload_element1.length()); request_body->AppendFileRange(upload_element2.path(), upload_element2.offset(), @@ -187,7 +184,7 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { upload = UploadDataStreamBuilder::Build(request_body.get(), - &blob_storage_controller, + &blob_storage_context, NULL, base::MessageLoopProxy::current().get()); ASSERT_EQ(4U, upload->element_readers().size()); @@ -203,11 +200,11 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { upload_element2.offset(), upload_element2.length(), upload_element2.expected_modification_time()); - request_body->AppendBlob(blob_url1); + request_body->AppendBlob(blob_id1); upload = UploadDataStreamBuilder::Build(request_body.get(), - &blob_storage_controller, + &blob_storage_context, NULL, base::MessageLoopProxy::current().get()); ASSERT_EQ(4U, upload->element_readers().size()); @@ -219,7 +216,7 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { // Test having one blob reference in the middle. request_body = new ResourceRequestBody(); request_body->AppendBytes(upload_element1.bytes(), upload_element1.length()); - request_body->AppendBlob(blob_url1); + request_body->AppendBlob(blob_id1); request_body->AppendFileRange(upload_element2.path(), upload_element2.offset(), upload_element2.length(), @@ -227,7 +224,7 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { upload = UploadDataStreamBuilder::Build(request_body.get(), - &blob_storage_controller, + &blob_storage_context, NULL, base::MessageLoopProxy::current().get()); ASSERT_EQ(4U, upload->element_readers().size()); @@ -238,10 +235,10 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { // Test having multiple blob references. request_body = new ResourceRequestBody(); - request_body->AppendBlob(blob_url1); + request_body->AppendBlob(blob_id1); request_body->AppendBytes(upload_element1.bytes(), upload_element1.length()); - request_body->AppendBlob(blob_url2); - request_body->AppendBlob(blob_url3); + request_body->AppendBlob(blob_id1); + request_body->AppendBlob(blob_id1); request_body->AppendFileRange(upload_element2.path(), upload_element2.offset(), upload_element2.length(), @@ -249,7 +246,7 @@ TEST(UploadDataStreamBuilderTest, ResolveBlobAndCreateUploadDataStream) { upload = UploadDataStreamBuilder::Build(request_body.get(), - &blob_storage_controller, + &blob_storage_context, NULL, base::MessageLoopProxy::current().get()); ASSERT_EQ(8U, upload->element_readers().size()); diff --git a/chromium/content/browser/media/android/OWNERS b/chromium/content/browser/media/android/OWNERS new file mode 100644 index 00000000000..5abf5a186b9 --- /dev/null +++ b/chromium/content/browser/media/android/OWNERS @@ -0,0 +1 @@ +qinmin@chromium.org diff --git a/chromium/content/browser/media/android/browser_demuxer_android.cc b/chromium/content/browser/media/android/browser_demuxer_android.cc new file mode 100644 index 00000000000..229b8858f1f --- /dev/null +++ b/chromium/content/browser/media/android/browser_demuxer_android.cc @@ -0,0 +1,111 @@ +// Copyright 2013 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/browser/media/android/browser_demuxer_android.h" + +#include "content/common/media/media_player_messages_android.h" + +namespace content { + +BrowserDemuxerAndroid::BrowserDemuxerAndroid() {} + +BrowserDemuxerAndroid::~BrowserDemuxerAndroid() {} + +void BrowserDemuxerAndroid::OverrideThreadForMessage( + const IPC::Message& message, + BrowserThread::ID* thread) { + switch (message.type()) { + case MediaPlayerHostMsg_DemuxerReady::ID: + case MediaPlayerHostMsg_ReadFromDemuxerAck::ID: + case MediaPlayerHostMsg_DurationChanged::ID: + case MediaPlayerHostMsg_MediaSeekRequestAck::ID: + *thread = BrowserThread::UI; + return; + } +} + +bool BrowserDemuxerAndroid::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(BrowserDemuxerAndroid, message, *message_was_ok) + IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DemuxerReady, OnDemuxerReady) + IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_ReadFromDemuxerAck, + OnReadFromDemuxerAck) + IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DurationChanged, + OnDurationChanged) + IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_MediaSeekRequestAck, + OnMediaSeekRequestAck) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void BrowserDemuxerAndroid::AddDemuxerClient( + int demuxer_client_id, + media::DemuxerAndroidClient* client) { + DVLOG(1) << __FUNCTION__ << " peer_pid=" << peer_pid() + << " demuxer_client_id=" << demuxer_client_id; + demuxer_clients_.AddWithID(client, demuxer_client_id); +} + +void BrowserDemuxerAndroid::RemoveDemuxerClient(int demuxer_client_id) { + DVLOG(1) << __FUNCTION__ << " peer_pid=" << peer_pid() + << " demuxer_client_id=" << demuxer_client_id; + demuxer_clients_.Remove(demuxer_client_id); +} + +void BrowserDemuxerAndroid::RequestDemuxerData( + int demuxer_client_id, media::DemuxerStream::Type type) { + DCHECK(demuxer_clients_.Lookup(demuxer_client_id)) << demuxer_client_id; + Send(new MediaPlayerMsg_ReadFromDemuxer(demuxer_client_id, type)); +} + +void BrowserDemuxerAndroid::RequestDemuxerSeek(int demuxer_client_id, + base::TimeDelta time_to_seek, + unsigned seek_request_id) { + DCHECK(demuxer_clients_.Lookup(demuxer_client_id)) << demuxer_client_id; + Send(new MediaPlayerMsg_MediaSeekRequest( + demuxer_client_id, time_to_seek, seek_request_id)); +} + +void BrowserDemuxerAndroid::RequestDemuxerConfigs(int demuxer_client_id) { + DCHECK(demuxer_clients_.Lookup(demuxer_client_id)) << demuxer_client_id; + Send(new MediaPlayerMsg_MediaConfigRequest(demuxer_client_id)); +} + +void BrowserDemuxerAndroid::OnDemuxerReady( + int demuxer_client_id, + const media::DemuxerConfigs& configs) { + media::DemuxerAndroidClient* client = + demuxer_clients_.Lookup(demuxer_client_id); + if (client) + client->OnDemuxerConfigsAvailable(configs); +} + +void BrowserDemuxerAndroid::OnReadFromDemuxerAck( + int demuxer_client_id, + const media::DemuxerData& data) { + media::DemuxerAndroidClient* client = + demuxer_clients_.Lookup(demuxer_client_id); + if (client) + client->OnDemuxerDataAvailable(data); +} + +void BrowserDemuxerAndroid::OnMediaSeekRequestAck(int demuxer_client_id, + unsigned seek_request_id) { + media::DemuxerAndroidClient* client = + demuxer_clients_.Lookup(demuxer_client_id); + if (client) + client->OnDemuxerSeeked(seek_request_id); +} + +void BrowserDemuxerAndroid::OnDurationChanged(int demuxer_client_id, + const base::TimeDelta& duration) { + media::DemuxerAndroidClient* client = + demuxer_clients_.Lookup(demuxer_client_id); + if (client) + client->OnDemuxerDurationChanged(duration); +} + +} // namespace content diff --git a/chromium/content/browser/media/android/browser_demuxer_android.h b/chromium/content/browser/media/android/browser_demuxer_android.h new file mode 100644 index 00000000000..6d17428f2d9 --- /dev/null +++ b/chromium/content/browser/media/android/browser_demuxer_android.h @@ -0,0 +1,61 @@ +// Copyright 2013 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_BROWSER_MEDIA_ANDROID_BROWSER_DEMUXER_ANDROID_H_ +#define CONTENT_BROWSER_MEDIA_ANDROID_BROWSER_DEMUXER_ANDROID_H_ + +#include "base/id_map.h" +#include "content/public/browser/browser_message_filter.h" +#include "media/base/android/demuxer_android.h" + +namespace content { + +// Represents the browser process half of an IPC-based implementation of +// media::DemuxerAndroid. +// +// Refer to RendererDemuxerAndroid for the renderer process half. +class CONTENT_EXPORT BrowserDemuxerAndroid + : public BrowserMessageFilter, + public media::DemuxerAndroid { + public: + BrowserDemuxerAndroid(); + + // BrowserMessageFilter overrides. + virtual void OverrideThreadForMessage(const IPC::Message& message, + BrowserThread::ID* thread) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) OVERRIDE; + + // media::DemuxerAndroid implementation. + virtual void AddDemuxerClient(int demuxer_client_id, + media::DemuxerAndroidClient* client) OVERRIDE; + virtual void RemoveDemuxerClient(int demuxer_client_id) OVERRIDE; + virtual void RequestDemuxerConfigs(int demuxer_client_id) OVERRIDE; + virtual void RequestDemuxerData(int demuxer_client_id, + media::DemuxerStream::Type type) OVERRIDE; + virtual void RequestDemuxerSeek(int demuxer_client_id, + base::TimeDelta time_to_seek, + unsigned seek_request_id) OVERRIDE; + + protected: + friend class base::RefCountedThreadSafe<BrowserDemuxerAndroid>; + virtual ~BrowserDemuxerAndroid(); + + private: + void OnDemuxerReady(int demuxer_client_id, + const media::DemuxerConfigs& configs); + void OnReadFromDemuxerAck(int demuxer_client_id, + const media::DemuxerData& data); + void OnMediaSeekRequestAck(int demuxer_client_id, unsigned seek_request_id); + void OnDurationChanged(int demuxer_client_id, + const base::TimeDelta& duration); + + IDMap<media::DemuxerAndroidClient> demuxer_clients_; + + DISALLOW_COPY_AND_ASSIGN(BrowserDemuxerAndroid); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_MEDIA_ANDROID_BROWSER_DEMUXER_ANDROID_H_ diff --git a/chromium/content/browser/android/browser_media_player_manager.cc b/chromium/content/browser/media/android/browser_media_player_manager.cc index dbf0c8f303d..f0129c13b24 100644 --- a/chromium/content/browser/android/browser_media_player_manager.cc +++ b/chromium/content/browser/media/android/browser_media_player_manager.cc @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/android/browser_media_player_manager.h" +#include "content/browser/media/android/browser_media_player_manager.h" +#include "base/command_line.h" #include "content/browser/android/content_view_core_impl.h" -#include "content/browser/android/media_resource_getter_impl.h" +#include "content/browser/media/android/browser_demuxer_android.h" +#include "content/browser/media/android/media_resource_getter_impl.h" +#include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_view_android.h" #include "content/common/media/media_player_messages_android.h" #include "content/public/browser/browser_context.h" @@ -13,42 +16,75 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" #include "media/base/android/media_drm_bridge.h" +#include "media/base/android/media_player_bridge.h" +#include "media/base/android/media_source_player.h" +#include "media/base/media_switches.h" using media::MediaDrmBridge; using media::MediaPlayerAndroid; +using media::MediaPlayerBridge; +using media::MediaPlayerManager; +using media::MediaSourcePlayer; // Threshold on the number of media players per renderer before we start // attempting to release inactive media players. static const int kMediaPlayerThreshold = 1; -namespace media { +namespace content { -static MediaPlayerManager::FactoryFunction g_factory_function = NULL; +static BrowserMediaPlayerManager::Factory g_factory = NULL; // static -CONTENT_EXPORT void MediaPlayerManager::RegisterFactoryFunction( - FactoryFunction factory_function) { - g_factory_function = factory_function; +void BrowserMediaPlayerManager::RegisterFactory(Factory factory) { + g_factory = factory; } // static -media::MediaPlayerManager* MediaPlayerManager::Create( - content::RenderViewHost* render_view_host) { - if (g_factory_function) - return g_factory_function(render_view_host); - return new content::BrowserMediaPlayerManager(render_view_host); +BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create( + RenderViewHost* rvh) { + if (g_factory) + return g_factory(rvh); + return new BrowserMediaPlayerManager(rvh); } -} // namespace media +#if !defined(GOOGLE_TV) +// static +MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer( + MediaPlayerHostMsg_Initialize_Type type, + int player_id, + const GURL& url, + const GURL& first_party_for_cookies, + int demuxer_client_id, + bool hide_url_log, + MediaPlayerManager* manager, + media::DemuxerAndroid* demuxer) { + switch (type) { + case MEDIA_PLAYER_TYPE_URL: { + MediaPlayerBridge* media_player_bridge = new MediaPlayerBridge( + player_id, url, first_party_for_cookies, hide_url_log, manager); + media_player_bridge->Initialize(); + return media_player_bridge; + } -namespace content { + case MEDIA_PLAYER_TYPE_MEDIA_SOURCE: { + return new MediaSourcePlayer( + player_id, manager, demuxer_client_id, demuxer); + } + } + + NOTREACHED(); + return NULL; +} +#endif BrowserMediaPlayerManager::BrowserMediaPlayerManager( RenderViewHost* render_view_host) : RenderViewHostObserver(render_view_host), fullscreen_player_id_(-1), - web_contents_(WebContents::FromRenderViewHost(render_view_host)) { + web_contents_(WebContents::FromRenderViewHost(render_view_host)), + weak_ptr_factory_(this) { } BrowserMediaPlayerManager::~BrowserMediaPlayerManager() {} @@ -67,14 +103,6 @@ bool BrowserMediaPlayerManager::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DestroyMediaPlayer, OnDestroyPlayer) IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DestroyAllMediaPlayers, DestroyAllMediaPlayers) - IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DemuxerReady, - OnDemuxerReady) - IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_ReadFromDemuxerAck, - OnReadFromDemuxerAck) - IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DurationChanged, - OnDurationChanged) - IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_MediaSeekRequestAck, - OnMediaSeekRequestAck) IPC_MESSAGE_HANDLER(MediaKeysHostMsg_InitializeCDM, OnInitializeCDM) IPC_MESSAGE_HANDLER(MediaKeysHostMsg_GenerateKeyRequest, @@ -103,7 +131,7 @@ void BrowserMediaPlayerManager::FullscreenPlayerPlay() { void BrowserMediaPlayerManager::FullscreenPlayerPause() { MediaPlayerAndroid* player = GetFullscreenPlayer(); if (player) { - player->Pause(); + player->Pause(true); Send(new MediaPlayerMsg_DidMediaPlayerPause( routing_id(), fullscreen_player_id_)); } @@ -194,11 +222,6 @@ void BrowserMediaPlayerManager::OnVideoSizeChanged( video_view_->OnVideoSizeChanged(width, height); } -void BrowserMediaPlayerManager::OnReadFromDemuxer( - int player_id, media::DemuxerStream::Type type) { - Send(new MediaPlayerMsg_ReadFromDemuxer(routing_id(), player_id, type)); -} - void BrowserMediaPlayerManager::RequestMediaResources(int player_id) { int num_active_player = 0; ScopedVector<MediaPlayerAndroid>::iterator it; @@ -269,22 +292,13 @@ MediaDrmBridge* BrowserMediaPlayerManager::GetDrmBridge(int media_keys_id) { void BrowserMediaPlayerManager::DestroyAllMediaPlayers() { players_.clear(); + drm_bridges_.clear(); if (fullscreen_player_id_ != -1) { video_view_.reset(); fullscreen_player_id_ = -1; } } -void BrowserMediaPlayerManager::OnMediaSeekRequest( - int player_id, base::TimeDelta time_to_seek, unsigned seek_request_id) { - Send(new MediaPlayerMsg_MediaSeekRequest( - routing_id(), player_id, time_to_seek, seek_request_id)); -} - -void BrowserMediaPlayerManager::OnMediaConfigRequest(int player_id) { - Send(new MediaPlayerMsg_MediaConfigRequest(routing_id(), player_id)); -} - void BrowserMediaPlayerManager::OnProtectedSurfaceRequested(int player_id) { if (fullscreen_player_id_ == player_id) return; @@ -376,16 +390,23 @@ void BrowserMediaPlayerManager::OnExitFullscreen(int player_id) { } void BrowserMediaPlayerManager::OnInitialize( + MediaPlayerHostMsg_Initialize_Type type, int player_id, const GURL& url, - media::MediaPlayerAndroid::SourceType source_type, - const GURL& first_party_for_cookies) { + const GURL& first_party_for_cookies, + int demuxer_client_id) { + DCHECK(type != MEDIA_PLAYER_TYPE_MEDIA_SOURCE || demuxer_client_id > 0) + << "Media source players must have positive demuxer client IDs: " + << demuxer_client_id; + RemovePlayer(player_id); - RenderProcessHost* host = render_view_host()->GetProcess(); - AddPlayer(media::MediaPlayerAndroid::Create( - player_id, url, source_type, first_party_for_cookies, - host->GetBrowserContext()->IsOffTheRecord(), this)); + RenderProcessHostImpl* host = + static_cast<RenderProcessHostImpl*>(render_view_host()->GetProcess()); + AddPlayer(CreateMediaPlayer( + type, player_id, url, first_party_for_cookies, demuxer_client_id, + host->GetBrowserContext()->IsOffTheRecord(), this, + host->browser_demuxer_android())); } void BrowserMediaPlayerManager::OnStart(int player_id) { @@ -400,10 +421,12 @@ void BrowserMediaPlayerManager::OnSeek(int player_id, base::TimeDelta time) { player->SeekTo(time); } -void BrowserMediaPlayerManager::OnPause(int player_id) { +void BrowserMediaPlayerManager::OnPause( + int player_id, + bool is_media_related_action) { MediaPlayerAndroid* player = GetPlayer(player_id); if (player) - player->Pause(); + player->Pause(is_media_related_action); } void BrowserMediaPlayerManager::OnSetVolume(int player_id, double volume) { @@ -434,33 +457,11 @@ void BrowserMediaPlayerManager::OnDestroyPlayer(int player_id) { fullscreen_player_id_ = -1; } -void BrowserMediaPlayerManager::OnDemuxerReady( - int player_id, - const media::MediaPlayerHostMsg_DemuxerReady_Params& params) { - MediaPlayerAndroid* player = GetPlayer(player_id); - if (player) - player->DemuxerReady(params); -} - -void BrowserMediaPlayerManager::OnReadFromDemuxerAck( - int player_id, - const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) { - MediaPlayerAndroid* player = GetPlayer(player_id); - if (player) - player->ReadFromDemuxerAck(params); -} - -void BrowserMediaPlayerManager::OnMediaSeekRequestAck( - int player_id, unsigned seek_request_id) { - MediaPlayerAndroid* player = GetPlayer(player_id); - if (player) - player->OnSeekRequestAck(seek_request_id); -} - void BrowserMediaPlayerManager::OnInitializeCDM( int media_keys_id, - const std::vector<uint8>& uuid) { - AddDrmBridge(media_keys_id, uuid); + const std::vector<uint8>& uuid, + const GURL& frame_url) { + AddDrmBridge(media_keys_id, uuid, frame_url); // In EME v0.1b MediaKeys lives in the media element. So the |media_keys_id| // is the same as the |player_id|. OnSetMediaKeys(media_keys_id, media_keys_id); @@ -470,9 +471,29 @@ void BrowserMediaPlayerManager::OnGenerateKeyRequest( int media_keys_id, const std::string& type, const std::vector<uint8>& init_data) { + if (CommandLine::ForCurrentProcess() + ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) { + GenerateKeyIfAllowed(media_keys_id, type, init_data, true); + return; + } + MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id); - if (drm_bridge) - drm_bridge->GenerateKeyRequest(type, &init_data[0], init_data.size()); + if (!drm_bridge) { + DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found"; + OnKeyError(media_keys_id, "", media::MediaKeys::kUnknownError, 0); + return; + } + + WebContents* web_contents = + WebContents::FromRenderViewHost(render_view_host()); + web_contents->GetDelegate()->RequestProtectedMediaIdentifierPermission( + web_contents, + drm_bridge->frame_url(), + base::Bind(&BrowserMediaPlayerManager::GenerateKeyIfAllowed, + weak_ptr_factory_.GetWeakPtr(), + media_keys_id, + type, + init_data)); } void BrowserMediaPlayerManager::OnAddKey(int media_keys_id, @@ -480,25 +501,33 @@ void BrowserMediaPlayerManager::OnAddKey(int media_keys_id, const std::vector<uint8>& init_data, const std::string& session_id) { MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id); - if (drm_bridge) { - drm_bridge->AddKey(&key[0], key.size(), &init_data[0], init_data.size(), - session_id); + if (!drm_bridge) { + DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found"; + OnKeyError(media_keys_id, session_id, media::MediaKeys::kUnknownError, 0); + return; } + + drm_bridge->AddKey(&key[0], key.size(), &init_data[0], init_data.size(), + session_id); + // In EME v0.1b MediaKeys lives in the media element. So the |media_keys_id| + // is the same as the |player_id|. + // TODO(xhwang): Separate |media_keys_id| and |player_id|. + MediaPlayerAndroid* player = GetPlayer(media_keys_id); + if (player) + player->OnKeyAdded(); } void BrowserMediaPlayerManager::OnCancelKeyRequest( int media_keys_id, const std::string& session_id) { MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id); - if (drm_bridge) - drm_bridge->CancelKeyRequest(session_id); -} + if (!drm_bridge) { + DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found"; + OnKeyError(media_keys_id, session_id, media::MediaKeys::kUnknownError, 0); + return; + } -void BrowserMediaPlayerManager::OnDurationChanged( - int player_id, const base::TimeDelta& duration) { - MediaPlayerAndroid* player = GetPlayer(player_id); - if (player) - player->DurationChanged(duration); + drm_bridge->CancelKeyRequest(session_id); } void BrowserMediaPlayerManager::AddPlayer(MediaPlayerAndroid* player) { @@ -516,12 +545,40 @@ void BrowserMediaPlayerManager::RemovePlayer(int player_id) { } } +scoped_ptr<media::MediaPlayerAndroid> BrowserMediaPlayerManager::SwapPlayer( + int player_id, media::MediaPlayerAndroid* player) { + media::MediaPlayerAndroid* previous_player = NULL; + for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin(); + it != players_.end(); ++it) { + if ((*it)->player_id() == player_id) { + previous_player = *it; + players_.weak_erase(it); + players_.push_back(player); + break; + } + } + return scoped_ptr<media::MediaPlayerAndroid>(previous_player); +} + void BrowserMediaPlayerManager::AddDrmBridge(int media_keys_id, - const std::vector<uint8>& uuid) { + const std::vector<uint8>& uuid, + const GURL& frame_url) { DCHECK(!GetDrmBridge(media_keys_id)); - scoped_ptr<MediaDrmBridge> drm_bridge( - MediaDrmBridge::Create(media_keys_id, uuid, this)); - DCHECK(drm_bridge) << "failed to create drm bridge. "; + // TODO(xhwang/ddorwin): Pass the security level from key system. + std::string security_level = "L3"; + if (CommandLine::ForCurrentProcess() + ->HasSwitch(switches::kMediaDrmEnableNonCompositing)) { + security_level = "L1"; + } + + scoped_ptr<MediaDrmBridge> drm_bridge(MediaDrmBridge::Create( + media_keys_id, uuid, frame_url, security_level, this)); + if (!drm_bridge) { + DVLOG(1) << "failed to create drm bridge."; + OnKeyError(media_keys_id, "", media::MediaKeys::kUnknownError, 0); + return; + } + drm_bridges_.push_back(drm_bridge.release()); } @@ -540,7 +597,7 @@ void BrowserMediaPlayerManager::OnSetMediaKeys(int player_id, MediaPlayerAndroid* player = GetPlayer(player_id); MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id); if (!player || !drm_bridge) { - NOTREACHED() << "OnSetMediaKeys(): Player and MediaKeys must be present."; + DVLOG(1) << "OnSetMediaKeys(): Player and MediaKeys must be present."; return; } // TODO(qinmin): add the logic to decide whether we should create the @@ -548,4 +605,22 @@ void BrowserMediaPlayerManager::OnSetMediaKeys(int player_id, player->SetDrmBridge(drm_bridge); } +void BrowserMediaPlayerManager::GenerateKeyIfAllowed( + int media_keys_id, + const std::string& type, + const std::vector<uint8>& init_data, + bool allowed) { + if (!allowed) + return; + + MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id); + if (!drm_bridge) { + DLOG(WARNING) << "No MediaDrmBridge for ID: " << media_keys_id << " found"; + OnKeyError(media_keys_id, "", media::MediaKeys::kUnknownError, 0); + return; + } + + drm_bridge->GenerateKeyRequest(type, &init_data[0], init_data.size()); +} + } // namespace content diff --git a/chromium/content/browser/android/browser_media_player_manager.h b/chromium/content/browser/media/android/browser_media_player_manager.h index 7f193e96175..a26e1829cf8 100644 --- a/chromium/content/browser/android/browser_media_player_manager.h +++ b/chromium/content/browser/media/android/browser_media_player_manager.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_ -#define CONTENT_BROWSER_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_ +#ifndef CONTENT_BROWSER_MEDIA_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_ +#define CONTENT_BROWSER_MEDIA_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_ #include <map> #include <string> @@ -15,14 +15,15 @@ #include "base/memory/scoped_vector.h" #include "base/time/time.h" #include "content/browser/android/content_video_view.h" +#include "content/common/media/media_player_messages_enums_android.h" #include "content/public/browser/render_view_host_observer.h" -#include "media/base/android/demuxer_stream_player_params.h" #include "media/base/android/media_player_android.h" #include "media/base/android/media_player_manager.h" #include "ui/gfx/rect_f.h" #include "url/gurl.h" namespace media { +class DemuxerAndroid; class MediaDrmBridge; } @@ -39,6 +40,13 @@ class CONTENT_EXPORT BrowserMediaPlayerManager : public RenderViewHostObserver, public media::MediaPlayerManager { public: + // Permits embedders to provide an extended version of the class. + typedef BrowserMediaPlayerManager* (*Factory)(RenderViewHost*); + static void RegisterFactory(Factory factory); + + // Returns a new instance using the registered factory if available. + static BrowserMediaPlayerManager* Create(RenderViewHost* rvh); + virtual ~BrowserMediaPlayerManager(); // RenderViewHostObserver overrides. @@ -68,8 +76,6 @@ class CONTENT_EXPORT BrowserMediaPlayerManager virtual void OnError(int player_id, int error) OVERRIDE; virtual void OnVideoSizeChanged( int player_id, int width, int height) OVERRIDE; - virtual void OnReadFromDemuxer(int player_id, - media::DemuxerStream::Type type) OVERRIDE; virtual void RequestMediaResources(int player_id) OVERRIDE; virtual void ReleaseMediaResources(int player_id) OVERRIDE; virtual media::MediaResourceGetter* GetMediaResourceGetter() OVERRIDE; @@ -77,9 +83,6 @@ class CONTENT_EXPORT BrowserMediaPlayerManager virtual media::MediaPlayerAndroid* GetPlayer(int player_id) OVERRIDE; virtual media::MediaDrmBridge* GetDrmBridge(int media_keys_id) OVERRIDE; virtual void DestroyAllMediaPlayers() OVERRIDE; - virtual void OnMediaSeekRequest(int player_id, base::TimeDelta time_to_seek, - unsigned seek_request_id) OVERRIDE; - virtual void OnMediaConfigRequest(int player_id) OVERRIDE; virtual void OnProtectedSurfaceRequested(int player_id) OVERRIDE; virtual void OnKeyAdded(int media_keys_id, const std::string& session_id) OVERRIDE; @@ -98,35 +101,27 @@ class CONTENT_EXPORT BrowserMediaPlayerManager #endif protected: - friend MediaPlayerManager* MediaPlayerManager::Create( - content::RenderViewHost*); - - // The instance of this class is supposed to be created by either Create() - // method of MediaPlayerManager or the derived classes constructors. + // Clients must use Create() or subclass constructor. explicit BrowserMediaPlayerManager(RenderViewHost* render_view_host); // Message handlers. virtual void OnEnterFullscreen(int player_id); virtual void OnExitFullscreen(int player_id); virtual void OnInitialize( + MediaPlayerHostMsg_Initialize_Type type, int player_id, const GURL& url, - media::MediaPlayerAndroid::SourceType source_type, - const GURL& first_party_for_cookies); + const GURL& first_party_for_cookies, + int demuxer_client_id); virtual void OnStart(int player_id); virtual void OnSeek(int player_id, base::TimeDelta time); - virtual void OnPause(int player_id); + virtual void OnPause(int player_id, bool is_media_related_action); virtual void OnSetVolume(int player_id, double volume); virtual void OnReleaseResources(int player_id); virtual void OnDestroyPlayer(int player_id); - virtual void OnDemuxerReady( - int player_id, - const media::MediaPlayerHostMsg_DemuxerReady_Params& params); - virtual void OnReadFromDemuxerAck( - int player_id, - const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params); - void OnMediaSeekRequestAck(int player_id, unsigned seek_request_id); - void OnInitializeCDM(int media_keys_id, const std::vector<uint8>& uuid); + void OnInitializeCDM(int media_keys_id, + const std::vector<uint8>& uuid, + const GURL& frame_url); void OnGenerateKeyRequest(int media_keys_id, const std::string& type, const std::vector<uint8>& init_data); @@ -135,7 +130,6 @@ class CONTENT_EXPORT BrowserMediaPlayerManager const std::vector<uint8>& init_data, const std::string& session_id); void OnCancelKeyRequest(int media_keys_id, const std::string& session_id); - void OnDurationChanged(int player_id, const base::TimeDelta& duration); void OnSetMediaKeys(int player_id, int media_keys_id); #if defined(GOOGLE_TV) @@ -149,13 +143,45 @@ class CONTENT_EXPORT BrowserMediaPlayerManager // Removes the player with the specified id. void RemovePlayer(int player_id); - // Add a new MediaDrmBridge for the given |uuid| and |media_keys_id|. - void AddDrmBridge(int media_keys_id, const std::vector<uint8>& uuid); + // Replaces a player with the specified id with a given MediaPlayerAndroid + // object. This will also return the original MediaPlayerAndroid object that + // was replaced. + scoped_ptr<media::MediaPlayerAndroid> SwapPlayer( + int player_id, + media::MediaPlayerAndroid* player); + + // Add a new MediaDrmBridge for the given |uuid|, |media_keys_id|, and + // |frame_url|. + void AddDrmBridge(int media_keys_id, + const std::vector<uint8>& uuid, + const GURL& frame_url); // Removes the DRM bridge with the specified id. void RemoveDrmBridge(int media_keys_id); private: + void GenerateKeyIfAllowed(int media_keys_id, + const std::string& type, + const std::vector<uint8>& init_data, + bool allowed); + + // Constructs a MediaPlayerAndroid object. Declared static to permit embedders + // to override functionality. + // + // Objects must call |manager->RequestMediaResources()| before decoding + // and |manager->ReleaseMediaSources()| after finishing. This allows the + // manager to track decoding resources across the process and free them as + // needed. + static media::MediaPlayerAndroid* CreateMediaPlayer( + MediaPlayerHostMsg_Initialize_Type type, + int player_id, + const GURL& url, + const GURL& first_party_for_cookies, + int demuxer_client_id, + bool hide_url_log, + media::MediaPlayerManager* manager, + media::DemuxerAndroid* demuxer); + // An array of managed players. ScopedVector<media::MediaPlayerAndroid> players_; @@ -174,9 +200,11 @@ class CONTENT_EXPORT BrowserMediaPlayerManager // Object for retrieving resources media players. scoped_ptr<media::MediaResourceGetter> media_resource_getter_; + base::WeakPtrFactory<BrowserMediaPlayerManager> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(BrowserMediaPlayerManager); }; } // namespace content -#endif // CONTENT_BROWSER_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_ +#endif // CONTENT_BROWSER_MEDIA_ANDROID_BROWSER_MEDIA_PLAYER_MANAGER_H_ diff --git a/chromium/content/browser/media/android/media_drm_credential_manager.cc b/chromium/content/browser/media/android/media_drm_credential_manager.cc new file mode 100644 index 00000000000..8151256dad6 --- /dev/null +++ b/chromium/content/browser/media/android/media_drm_credential_manager.cc @@ -0,0 +1,109 @@ +// Copyright 2013 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/browser/media/android/media_drm_credential_manager.h" + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "jni/MediaDrmCredentialManager_jni.h" +#include "media/base/android/media_drm_bridge.h" +#include "url/gurl.h" + +using base::android::ScopedJavaGlobalRef; + +namespace { + +void MediaDrmCredentialManagerCallback( + const ScopedJavaGlobalRef<jobject>& j_media_drm_credential_manager_callback, + bool succeeded) { + JNIEnv* env = base::android::AttachCurrentThread(); + content::Java_MediaDrmCredentialManagerCallback_onCredentialResetFinished( + env, j_media_drm_credential_manager_callback.obj(), succeeded); +} + +} // namespace + +namespace content { + +// TODO(qinmin): Move the UUID definition to some common places. +static const uint8 kWidevineUuid[16] = { + 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, + 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED }; + +MediaDrmCredentialManager::MediaDrmCredentialManager() {}; + +MediaDrmCredentialManager::~MediaDrmCredentialManager() {}; + +// static +MediaDrmCredentialManager* MediaDrmCredentialManager::GetInstance() { + return Singleton<MediaDrmCredentialManager>::get(); +} + +void MediaDrmCredentialManager::ResetCredentials( + const ResetCredentialsCB& callback) { + // Ignore reset request if one is already in progress. + if (!reset_credentials_cb_.is_null()) + return; + + reset_credentials_cb_ = callback; + + // First reset the L3 credential. + if (!ResetCredentialsInternal("L3")) { + // TODO(qinmin): We should post a task instead. + base::ResetAndReturn(&reset_credentials_cb_).Run(false); + } +} + +// static +void ResetCredentials( + JNIEnv* env, + jclass clazz, + jobject j_media_drm_credential_manager_callback) { + MediaDrmCredentialManager* media_drm_credential_manager = + MediaDrmCredentialManager::GetInstance(); + + ScopedJavaGlobalRef<jobject> j_scoped_media_drm_credential_manager_callback; + j_scoped_media_drm_credential_manager_callback.Reset( + env, j_media_drm_credential_manager_callback); + + MediaDrmCredentialManager::ResetCredentialsCB callback_runner = + base::Bind(&MediaDrmCredentialManagerCallback, + j_scoped_media_drm_credential_manager_callback); + + media_drm_credential_manager->ResetCredentials(callback_runner); +} + +void MediaDrmCredentialManager::OnResetCredentialsCompleted( + const std::string& security_level, bool success) { + if (security_level == "L3" && success) { + if (ResetCredentialsInternal("L1")) + return; + success = false; + } + + base::ResetAndReturn(&reset_credentials_cb_).Run(success); + media_drm_bridge_.reset(); +} + +bool MediaDrmCredentialManager::ResetCredentialsInternal( + const std::string& security_level) { + std::vector<uint8> uuid(kWidevineUuid, kWidevineUuid + 16); + media_drm_bridge_ = media::MediaDrmBridge::Create( + 0, uuid, GURL(), security_level, NULL); + if (!media_drm_bridge_) + return false; + media_drm_bridge_->ResetDeviceCredentials( + base::Bind(&MediaDrmCredentialManager::OnResetCredentialsCompleted, + base::Unretained(this), security_level)); + return true; +} + +// static +bool MediaDrmCredentialManager::RegisterMediaDrmCredentialManager(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace content diff --git a/chromium/content/browser/media/android/media_drm_credential_manager.h b/chromium/content/browser/media/android/media_drm_credential_manager.h new file mode 100644 index 00000000000..6b6845e2b80 --- /dev/null +++ b/chromium/content/browser/media/android/media_drm_credential_manager.h @@ -0,0 +1,60 @@ +// Copyright 2013 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_BROWSER_MEDIA_ANDROID_MEDIA_DRM_CREDENTIAL_MANAGER_H_ +#define CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_DRM_CREDENTIAL_MANAGER_H_ + +#include <jni.h> +#include <string> + +#include "base/callback.h" +#include "base/memory/singleton.h" +#include "media/base/android/media_drm_bridge.h" + +namespace content { + +// This class manages the media DRM credentials on Android. +class MediaDrmCredentialManager { + public: + static MediaDrmCredentialManager* GetInstance(); + + typedef base::Callback<void(bool)> ResetCredentialsCB; + + // Called to reset the DRM credentials. (for Java) + static void ResetCredentials(JNIEnv* env, jclass clazz, jobject callback); + + // Called to reset the DRM credentials. + void ResetCredentials(const ResetCredentialsCB& callback); + + static bool RegisterMediaDrmCredentialManager(JNIEnv* env); + + private: + friend struct DefaultSingletonTraits<MediaDrmCredentialManager>; + friend class Singleton<MediaDrmCredentialManager>; + + MediaDrmCredentialManager(); + ~MediaDrmCredentialManager(); + + + // Callback function passed to MediaDrmBridge. It is called when reset + // completed. + void OnResetCredentialsCompleted(const std::string& security_level, + bool success); + + // Reset DRM credentials for a particular security level. Returns false if + // we fail to create the MediaDrmBridge, or true otherwise. + bool ResetCredentialsInternal(const std::string& security_level); + + // The MediaDrmBridge object used to perform the credential reset. + scoped_ptr<media::MediaDrmBridge> media_drm_bridge_; + + // The callback provided by the caller. + ResetCredentialsCB reset_credentials_cb_; + + DISALLOW_COPY_AND_ASSIGN(MediaDrmCredentialManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_DRM_CREDENTIAL_MANAGER_H_ diff --git a/chromium/content/browser/android/media_resource_getter_impl.cc b/chromium/content/browser/media/android/media_resource_getter_impl.cc index 49703101b24..53a5094dce4 100644 --- a/chromium/content/browser/android/media_resource_getter_impl.cc +++ b/chromium/content/browser/media/android/media_resource_getter_impl.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 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/browser/android/media_resource_getter_impl.h" +#include "content/browser/media/android/media_resource_getter_impl.h" #include "base/android/jni_android.h" #include "base/android/jni_string.h" diff --git a/chromium/content/browser/android/media_resource_getter_impl.h b/chromium/content/browser/media/android/media_resource_getter_impl.h index 1d152447e7c..2b6e42ea313 100644 --- a/chromium/content/browser/android/media_resource_getter_impl.h +++ b/chromium/content/browser/media/android/media_resource_getter_impl.h @@ -1,9 +1,9 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 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_BROWSER_ANDROID_MEDIA_RESOURCE_GETTER_IMPL_H_ -#define CONTENT_BROWSER_ANDROID_MEDIA_RESOURCE_GETTER_IMPL_H_ +#ifndef CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_RESOURCE_GETTER_IMPL_H_ +#define CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_RESOURCE_GETTER_IMPL_H_ #include <jni.h> #include <string> @@ -82,4 +82,4 @@ class MediaResourceGetterImpl : public media::MediaResourceGetter { } // namespace content -#endif // CONTENT_BROWSER_ANDROID_MEDIA_RESOURCE_GETTER_IMPL_H_ +#endif // CONTENT_BROWSER_MEDIA_ANDROID_MEDIA_RESOURCE_GETTER_IMPL_H_ diff --git a/chromium/content/browser/media/encrypted_media_browsertest.cc b/chromium/content/browser/media/encrypted_media_browsertest.cc index ce5dc4e6ba0..b3df24d643b 100644 --- a/chromium/content/browser/media/encrypted_media_browsertest.cc +++ b/chromium/content/browser/media/encrypted_media_browsertest.cc @@ -9,30 +9,10 @@ #include "content/browser/media/media_browsertest.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" - -#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. - -#if defined(WIDEVINE_CDM_AVAILABLE) && defined(OS_LINUX) -#include <gnu/libc-version.h> -#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(OS_LINUX) - -#if defined(ENABLE_PEPPER_CDMS) -// Platform-specific filename relative to the chrome executable. -static const char kClearKeyCdmAdapterFileName[] = -#if defined(OS_MACOSX) - "clearkeycdmadapter.plugin"; -#elif defined(OS_WIN) - "clearkeycdmadapter.dll"; -#elif defined(OS_POSIX) - "libclearkeycdmadapter.so"; -#endif -#endif // defined(ENABLE_PEPPER_CDMS) +#include "content/shell/browser/shell.h" // Available key systems. static const char kClearKeyKeySystem[] = "webkit-org.w3.clearkey"; -static const char kExternalClearKeyKeySystem[] = - "org.chromium.externalclearkey"; // Supported media types. static const char kWebMAudioOnly[] = "audio/webm; codecs=\"vorbis\""; @@ -41,8 +21,9 @@ static const char kWebMAudioVideo[] = "video/webm; codecs=\"vorbis, vp8\""; static const char kMP4AudioOnly[] = "audio/mp4; codecs=\"mp4a.40.2\""; static const char kMP4VideoOnly[] = "video/mp4; codecs=\"avc1.4D4041\""; -// Common test expectations. -static const char kKeyError[] = "KEYERROR"; +// EME-specific test results and errors. +static const char kEmeGkrException[] = "GENERATE_KEY_REQUEST_EXCEPTION"; +static const char kEmeKeyError[] = "KEYERROR"; // The type of video src used to load media. enum SrcType { @@ -105,109 +86,25 @@ class EncryptedMediaTest : public content::MediaBrowserTest, } protected: -#if defined(ENABLE_PEPPER_CDMS) - virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { - RegisterPepperCdm(command_line, kClearKeyCdmAdapterFileName, - kExternalClearKeyKeySystem); - } - - virtual void RegisterPepperCdm(CommandLine* command_line, - const std::string& adapter_name, - const std::string& key_system) { - // Append the switch to register the Clear Key CDM Adapter. - base::FilePath plugin_dir; - EXPECT_TRUE(PathService::Get(base::DIR_MODULE, &plugin_dir)); - base::FilePath plugin_lib = plugin_dir.AppendASCII(adapter_name); - EXPECT_TRUE(base::PathExists(plugin_lib)); - base::FilePath::StringType pepper_plugin = plugin_lib.value(); - pepper_plugin.append(FILE_PATH_LITERAL("#CDM#0.1.0.0;")); -#if defined(OS_WIN) - pepper_plugin.append(ASCIIToWide(GetPepperType(key_system))); -#else - pepper_plugin.append(GetPepperType(key_system)); -#endif - command_line->AppendSwitchNative(switches::kRegisterPepperPlugins, - pepper_plugin); - } - - // Adapted from key_systems.cc. - std::string GetPepperType(const std::string& key_system) { - if (key_system == kExternalClearKeyKeySystem) - return "application/x-ppapi-clearkey-cdm"; -#if defined(WIDEVINE_CDM_AVAILABLE) - if (key_system == kWidevineKeySystem) - return "application/x-ppapi-widevine-cdm"; -#endif // WIDEVINE_CDM_AVAILABLE - - NOTREACHED(); - return ""; + // We want to fail quickly when a test fails because an error is encountered. + virtual void AddWaitForTitles(content::TitleWatcher* title_watcher) OVERRIDE { + MediaBrowserTest::AddWaitForTitles(title_watcher); + title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEmeGkrException)); + title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEmeKeyError)); } -#endif // defined(ENABLE_PEPPER_CDMS) }; -#if defined(WIDEVINE_CDM_AVAILABLE) -class WVEncryptedMediaTest : public EncryptedMediaTest { - public: - // Tests that the following happen after trying to play encrypted media: - // - webkitneedkey event is fired. - // - webkitGenerateKeyRequest() does not fail. - // - webkitkeymessage is fired - // - webkitAddKey() generates a WebKitKeyError since no real WV key is added. - void TestMSESimplePlayback(const char* encrypted_media, - const char* media_type, const char* key_system) { - // TODO(shadi): Remove after bots upgrade to precise. - // Don't run on lucid bots since the CDM is not compatible (glibc < 2.14) -#if defined(OS_LINUX) - if (strcmp(gnu_get_libc_version(), "2.11.1") == 0) { - LOG(INFO) << "Skipping test; not supported on glibc version: " - << gnu_get_libc_version(); - return; - } -#endif // defined(OS_LINUX) - EncryptedMediaTest::TestMSESimplePlayback(encrypted_media, media_type, - key_system, kKeyError); - bool receivedKeyMessage = false; - EXPECT_TRUE(ExecuteScriptAndExtractBool( - shell()->web_contents(), - "window.domAutomationController.send(video.receivedKeyMessage);", - &receivedKeyMessage)); - ASSERT_TRUE(receivedKeyMessage); - } - - protected: -#if defined(ENABLE_PEPPER_CDMS) - virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { - RegisterPepperCdm(command_line, kWidevineCdmAdapterFileName, - kWidevineKeySystem); - } -#endif // defined(ENABLE_PEPPER_CDMS) -}; -#endif // defined(WIDEVINE_CDM_AVAILABLE) - INSTANTIATE_TEST_CASE_P(ClearKey, EncryptedMediaTest, ::testing::Combine( ::testing::Values(kClearKeyKeySystem), ::testing::Values(SRC, MSE))); -// External Clear Key is currently only used on platforms that use Pepper CDMs. -#if defined(ENABLE_PEPPER_CDMS) -INSTANTIATE_TEST_CASE_P(ExternalClearKey, EncryptedMediaTest, - ::testing::Combine( - ::testing::Values(kExternalClearKeyKeySystem), - ::testing::Values(SRC, MSE))); - -IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, ConfigChangeVideo_ExternalClearKey) { - TestConfigChange(kExternalClearKeyKeySystem, kEnded); -} -#endif // defined(ENABLE_PEPPER_CDMS) - IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, ConfigChangeVideo_ClearKey) { TestConfigChange(kClearKeyKeySystem, kEnded); } IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, InvalidKeySystem) { TestMSESimplePlayback("bear-320x240-av-enc_av.webm", kWebMAudioVideo, - "com.example.invalid", - "GENERATE_KEY_REQUEST_EXCEPTION"); + "com.example.invalid", kEmeGkrException); } IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_WebM) { @@ -243,68 +140,11 @@ IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, FrameChangeVideo) { TestFrameSizeChange(GetParam(), kEnded); } -#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) -IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4) { - std::tr1::tuple<const char*, SrcType> test_params = GetParam(); - // MP4 without MSE is not support yet, http://crbug.com/170793. - if (std::tr1::get<1>(test_params) != MSE) { - LOG(INFO) << "Skipping test; Can only play MP4 encrypted streams by MSE."; - return; - } - TestMSESimplePlayback("bear-640x360-v_frag-cenc.mp4", kMP4VideoOnly, - std::tr1::get<0>(test_params), kEnded); -} - -IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_MP4) { - std::tr1::tuple<const char*, SrcType> test_params = GetParam(); - // MP4 without MSE is not support yet, http://crbug.com/170793. - if (std::tr1::get<1>(test_params) != MSE) { - LOG(INFO) << "Skipping test; Can only play MP4 encrypted streams by MSE."; - return; - } - TestMSESimplePlayback("bear-640x360-a_frag-cenc.mp4", kMP4AudioOnly, - std::tr1::get<0>(test_params), kEnded); -} -#endif - -// Run only when WV CDM is available. -#if defined(WIDEVINE_CDM_AVAILABLE) -IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_AudioOnly_WebM) { - TestMSESimplePlayback("bear-a-enc_a.webm", kWebMAudioOnly, - kWidevineKeySystem); -} - -IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_AudioClearVideo_WebM) { - TestMSESimplePlayback("bear-320x240-av-enc_a.webm", kWebMAudioVideo, - kWidevineKeySystem); -} - -IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_VideoAudio_WebM) { - TestMSESimplePlayback("bear-320x240-av-enc_av.webm", kWebMAudioVideo, - kWidevineKeySystem); -} - -IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_VideoOnly_WebM) { - TestMSESimplePlayback("bear-320x240-v-enc_v.webm", kWebMVideoOnly, - kWidevineKeySystem); -} - -IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_VideoClearAudio_WebM) { - TestMSESimplePlayback("bear-320x240-av-enc_v.webm", kWebMAudioVideo, - kWidevineKeySystem); -} - -#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) -IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_VideoOnly_MP4) { - TestMSESimplePlayback("bear-640x360-v_frag-cenc.mp4", kMP4VideoOnly, - kWidevineKeySystem); -} - -IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_AudioOnly_MP4) { - TestMSESimplePlayback("bear-640x360-a_frag-cenc.mp4", kMP4AudioOnly, - kWidevineKeySystem); +// TODO(shadi): Do we need both this and InvalidKeySystem? +IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, UnknownKeySystemThrowsException) { + RunEncryptedMediaTest("encrypted_media_player.html", "bear-a-enc_a.webm", + kWebMAudioOnly, "com.example.foo", SRC, + kEmeGkrException); } -#endif // defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) -#endif // defined(WIDEVINE_CDM_AVAILABLE) } // namespace content diff --git a/chromium/content/browser/media/media_browsertest.cc b/chromium/content/browser/media/media_browsertest.cc index 1d830354c9e..c264ec949fc 100644 --- a/chromium/content/browser/media/media_browsertest.cc +++ b/chromium/content/browser/media/media_browsertest.cc @@ -9,7 +9,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test_utils.h" // TODO(wolenetz): Fix Media.YUV* tests on MSVS 2012 x64. crbug.com/180074 @@ -59,16 +59,20 @@ void MediaBrowserTest::RunMediaTestPage( } void MediaBrowserTest::RunTest(const GURL& gurl, const char* expected) { - const string16 kExpected = ASCIIToUTF16(expected); + const string16 expected_title = ASCIIToUTF16(expected); DVLOG(1) << "Running test URL: " << gurl; - TitleWatcher title_watcher(shell()->web_contents(), kExpected); - title_watcher.AlsoWaitForTitle(ASCIIToUTF16(kEnded)); - title_watcher.AlsoWaitForTitle(ASCIIToUTF16(kError)); - title_watcher.AlsoWaitForTitle(ASCIIToUTF16(kFailed)); + TitleWatcher title_watcher(shell()->web_contents(), expected_title); + AddWaitForTitles(&title_watcher); NavigateToURL(shell(), gurl); string16 final_title = title_watcher.WaitAndGetTitle(); - EXPECT_EQ(kExpected, final_title); + EXPECT_EQ(expected_title, final_title); +} + +void MediaBrowserTest::AddWaitForTitles(content::TitleWatcher* title_watcher) { + title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kEnded)); + title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kError)); + title_watcher->AlsoWaitForTitle(ASCIIToUTF16(kFailed)); } // Tests playback and seeking of an audio or video file over file or http based @@ -117,7 +121,7 @@ IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearSilentWebm) { PlayVideo("bear_silent.webm", GetParam()); } -#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) +#if defined(USE_PROPRIETARY_CODECS) IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearMp4) { PlayVideo("bear.mp4", GetParam()); } @@ -139,7 +143,7 @@ IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearMovPcmS24be) { #endif #if defined(OS_CHROMEOS) -#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) +#if defined(USE_PROPRIETARY_CODECS) IN_PROC_BROWSER_TEST_P(MediaTest, VideoBearAviMp3Mpeg4) { PlayVideo("bear_mpeg4_mp3.avi", GetParam()); } @@ -217,7 +221,7 @@ IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv420pVp8)) { RunColorFormatTest("yuv420p.webm", "ENDED"); } -#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) +#if defined(USE_PROPRIETARY_CODECS) IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv420pH264)) { RunColorFormatTest("yuv420p.mp4", "ENDED"); } @@ -239,7 +243,7 @@ IN_PROC_BROWSER_TEST_F(MediaTest, MAYBE(Yuv444pH264)) { IN_PROC_BROWSER_TEST_F(MediaTest, Yuv420pMpeg4) { RunColorFormatTest("yuv420p.avi", "ENDED"); } -#endif -#endif +#endif // defined(OS_CHROMEOS) +#endif // defined(USE_PROPRIETARY_CODECS) } // namespace content diff --git a/chromium/content/browser/media/media_browsertest.h b/chromium/content/browser/media/media_browsertest.h index 013283b1ed6..6637ead2706 100644 --- a/chromium/content/browser/media/media_browsertest.h +++ b/chromium/content/browser/media/media_browsertest.h @@ -2,21 +2,30 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifndef CONTENT_BROWSER_MEDIA_MEDIA_BROWSERTEST_H_ +#define CONTENT_BROWSER_MEDIA_MEDIA_BROWSERTEST_H_ + +#include <utility> +#include <vector> + #include "content/test/content_browser_test.h" namespace content { +class TitleWatcher; + // Class used to automate running media related browser tests. The functions // assume that media files are located under files/media/ folder known to // the test http server. class MediaBrowserTest : public ContentBrowserTest { public: + typedef std::pair<const char*, const char*> StringPair; + + // Common test results. static const char kEnded[]; static const char kError[]; static const char kFailed[]; - typedef std::pair<const char*, const char*> StringPair; - virtual void SetUp() OVERRIDE; // Runs a html page with a list of URL query parameters. @@ -30,6 +39,10 @@ class MediaBrowserTest : public ContentBrowserTest { // Opens a URL and waits for the document title to match either one of the // default strings or the expected string. void RunTest(const GURL& gurl, const char* expected); + + virtual void AddWaitForTitles(content::TitleWatcher* title_watcher); }; } // namespace content + +#endif // CONTENT_BROWSER_MEDIA_MEDIA_BROWSERTEST_H_ diff --git a/chromium/content/browser/media/media_internals.cc b/chromium/content/browser/media/media_internals.cc index 273eeaeaf9f..22f164ddccc 100644 --- a/chromium/content/browser/media/media_internals.cc +++ b/chromium/content/browser/media/media_internals.cc @@ -9,6 +9,7 @@ #include "base/strings/stringprintf.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_ui.h" +#include "media/audio/audio_parameters.h" #include "media/base/media_log.h" #include "media/base/media_log_event.h" @@ -20,11 +21,15 @@ MediaInternals* MediaInternals::GetInstance() { MediaInternals::~MediaInternals() {} +namespace { +std::string FormatAudioStreamName(void* host, int stream_id) { + return base::StringPrintf("audio_streams.%p:%d", host, stream_id); +} +} + void MediaInternals::OnDeleteAudioStream(void* host, int stream_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - std::string stream = base::StringPrintf("audio_streams.%p:%d", - host, stream_id); - DeleteItem(stream); + DeleteItem(FormatAudioStreamName(host, stream_id)); } void MediaInternals::OnSetAudioStreamPlaying( @@ -34,8 +39,55 @@ void MediaInternals::OnSetAudioStreamPlaying( "playing", new base::FundamentalValue(playing)); } -void MediaInternals::OnSetAudioStreamStatus( - void* host, int stream_id, const std::string& status) { +void MediaInternals::OnAudioStreamCreated(void* host, + int stream_id, + const media::AudioParameters& params, + const std::string& input_device_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + StoreAudioStream(host, + stream_id, + "input_device_id", + Value::CreateStringValue(input_device_id)); + + StoreAudioStream( + host, stream_id, "status", Value::CreateStringValue("created")); + + StoreAudioStream( + host, stream_id, "stream_id", Value::CreateIntegerValue(stream_id)); + + StoreAudioStream(host, + stream_id, + "input_channels", + Value::CreateIntegerValue(params.input_channels())); + + StoreAudioStream(host, + stream_id, + "frames_per_buffer", + Value::CreateIntegerValue(params.frames_per_buffer())); + + StoreAudioStream(host, + stream_id, + "sample_rate", + Value::CreateIntegerValue(params.sample_rate())); + + StoreAudioStream(host, + stream_id, + "output_channels", + Value::CreateIntegerValue(params.channels())); + + StoreAudioStream( + host, + stream_id, + "channel_layout", + Value::CreateStringValue(ChannelLayoutToString(params.channel_layout()))); + + SendEverything(); +} + +void MediaInternals::OnSetAudioStreamStatus(void* host, + int stream_id, + const std::string& status) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); UpdateAudioStream(host, stream_id, "status", new base::StringValue(status)); @@ -92,13 +144,21 @@ void MediaInternals::SendEverything() { MediaInternals::MediaInternals() { } +void MediaInternals::StoreAudioStream(void* host, + int stream_id, + const std::string& property, + base::Value* value) { + StoreItem(FormatAudioStreamName(host, stream_id), property, value); +} + void MediaInternals::UpdateAudioStream(void* host, int stream_id, const std::string& property, base::Value* value) { - std::string stream = base::StringPrintf("audio_streams.%p:%d", - host, stream_id); - UpdateItem("media.addAudioStream", stream, property, value); + UpdateItem("media.updateAudioStream", + FormatAudioStreamName(host, stream_id), + property, + value); } void MediaInternals::DeleteItem(const std::string& item) { @@ -107,9 +167,9 @@ void MediaInternals::DeleteItem(const std::string& item) { SendUpdate("media.onItemDeleted", value.get()); } -void MediaInternals::UpdateItem( - const std::string& update_fn, const std::string& id, - const std::string& property, base::Value* value) { +base::DictionaryValue* MediaInternals::StoreItem(const std::string& id, + const std::string& property, + base::Value* value) { base::DictionaryValue* item_properties; if (!data_.GetDictionary(id, &item_properties)) { item_properties = new base::DictionaryValue(); @@ -117,6 +177,14 @@ void MediaInternals::UpdateItem( item_properties->SetString("id", id); } item_properties->Set(property, value); + return item_properties; +} + +void MediaInternals::UpdateItem(const std::string& update_fn, + const std::string& id, + const std::string& property, + base::Value* value) { + base::DictionaryValue* item_properties = StoreItem(id, property, value); SendUpdate(update_fn, item_properties); } diff --git a/chromium/content/browser/media/media_internals.h b/chromium/content/browser/media/media_internals.h index 4a4d2efc7d1..24b314ef321 100644 --- a/chromium/content/browser/media/media_internals.h +++ b/chromium/content/browser/media/media_internals.h @@ -16,6 +16,7 @@ #include "content/public/common/media_stream_request.h" namespace media { +class AudioParameters; struct MediaLogEvent; } @@ -36,8 +37,14 @@ class CONTENT_EXPORT MediaInternals { virtual void OnDeleteAudioStream(void* host, int stream_id); // Called when an audio stream is set to playing or paused. - virtual void OnSetAudioStreamPlaying(void* host, int stream_id, - bool playing); + virtual void OnSetAudioStreamPlaying(void* host, int stream_id, bool playing); + + // Called when an audio stream is created with the parameters that + // it attempts to use to make the stream. + virtual void OnAudioStreamCreated(void* host, + int stream_id, + const media::AudioParameters& params, + const std::string& input_device_id); // Called when the status of an audio stream is set to "created", "closed", or // "error". @@ -73,9 +80,21 @@ class CONTENT_EXPORT MediaInternals { void UpdateAudioStream(void* host, int stream_id, const std::string& property, base::Value* value); + // See UpdateAudioStream. The difference is that StoreAudioStream does not + // immediately send the data to the client, instead it waits until + // SendEverything is called. + void StoreAudioStream(void* host, + int stream_id, + const std::string& property, + base::Value* value); + // Removes |item| from |data_|. void DeleteItem(const std::string& item); + base::DictionaryValue* StoreItem(const std::string& id, + const std::string& property, + base::Value* value); + // Sets data_.id.property = value and notifies attached UIs using update_fn. // id may be any depth, e.g. "video.decoders.1.2.3" void UpdateItem(const std::string& update_fn, const std::string& id, diff --git a/chromium/content/browser/media/media_internals_handler.cc b/chromium/content/browser/media/media_internals_handler.cc index 1ea6b04c663..1715330e8e5 100644 --- a/chromium/content/browser/media/media_internals_handler.cc +++ b/chromium/content/browser/media/media_internals_handler.cc @@ -7,7 +7,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/values.h" -#include "content/browser//media/media_internals_proxy.h" +#include "content/browser/media/media_internals_proxy.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" diff --git a/chromium/content/browser/media/media_internals_ui.cc b/chromium/content/browser/media/media_internals_ui.cc index 83638148bf9..f185bdca23e 100644 --- a/chromium/content/browser/media/media_internals_ui.cc +++ b/chromium/content/browser/media/media_internals_ui.cc @@ -21,12 +21,6 @@ WebUIDataSource* CreateMediaInternalsHTMLSource() { WebUIDataSource::Create(kChromeUIMediaInternalsHost); source->SetJsonPath("strings.js"); - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableNewMediaInternals)) { - source->AddResourcePath("media_internals.js", IDR_MEDIA_INTERNALS_NEW_JS); - source->SetDefaultResource(IDR_MEDIA_INTERNALS_NEW_HTML); - return source; - } source->AddResourcePath("media_internals.js", IDR_MEDIA_INTERNALS_JS); source->SetDefaultResource(IDR_MEDIA_INTERNALS_HTML); diff --git a/chromium/content/browser/media/media_internals_unittest.cc b/chromium/content/browser/media/media_internals_unittest.cc index 58c8f91dc68..173ba36539f 100644 --- a/chromium/content/browser/media/media_internals_unittest.cc +++ b/chromium/content/browser/media/media_internals_unittest.cc @@ -7,7 +7,10 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/strings/stringprintf.h" #include "content/public/test/test_browser_thread.h" +#include "media/audio/audio_parameters.h" +#include "media/base/channel_layout.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -58,6 +61,46 @@ class MediaInternalsTest : public testing::Test { scoped_ptr<MediaInternals> internals_; }; +TEST_F(MediaInternalsTest, AudioStreamCreatedSendsMessage) { + media::AudioParameters params = + media::AudioParameters(media::AudioParameters::AUDIO_PCM_LINEAR, + media::CHANNEL_LAYOUT_MONO, + 48000, + 16, + 129); + + const int stream_id = 0; + const std::string device_id = "test"; + const std::string name = + base::StringPrintf("audio_streams.%p:%d", this, stream_id); + + internals_->OnAudioStreamCreated(this, stream_id, params, device_id); + + std::string channel_layout; + data()->GetString(name + ".channel_layout", &channel_layout); + EXPECT_EQ("MONO", channel_layout); + + int sample_rate; + data()->GetInteger(name + ".sample_rate", &sample_rate); + EXPECT_EQ(params.sample_rate(), sample_rate); + + int frames_per_buffer; + data()->GetInteger(name + ".frames_per_buffer", &frames_per_buffer); + EXPECT_EQ(params.frames_per_buffer(), frames_per_buffer); + + int output_channels; + data()->GetInteger(name + ".output_channels", &output_channels); + EXPECT_EQ(params.channels(), output_channels); + + std::string device_id_out; + data()->GetString(name + ".input_device_id", &device_id_out); + EXPECT_EQ(device_id, device_id_out); + + int input_channels; + data()->GetInteger(name + ".input_channels", &input_channels); + EXPECT_EQ(params.input_channels(), input_channels); +} + TEST_F(MediaInternalsTest, UpdateAddsNewItem) { UpdateItem("some.item", "testing", new base::FundamentalValue(true)); bool testing = false; diff --git a/chromium/content/browser/media/webrtc_browsertest.cc b/chromium/content/browser/media/webrtc_browsertest.cc index 58b3d3fdcfc..6b75262c66c 100644 --- a/chromium/content/browser/media/webrtc_browsertest.cc +++ b/chromium/content/browser/media/webrtc_browsertest.cc @@ -8,7 +8,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -19,16 +19,21 @@ namespace { -std::string GenerateGetUserMediaCall(int min_width, +static const char kGetUserMedia[] = "getUserMedia"; +static const char kGetUserMediaWithAnalysis[] = "getUserMediaWithAnalysis"; + +std::string GenerateGetUserMediaCall(const char* function_name, + int min_width, int max_width, int min_height, int max_height, int min_frame_rate, int max_frame_rate) { return base::StringPrintf( - "getUserMedia({video: {mandatory: {minWidth: %d, maxWidth: %d, " + "%s({video: {mandatory: {minWidth: %d, maxWidth: %d, " "minHeight: %d, maxHeight: %d, minFrameRate: %d, maxFrameRate: %d}, " "optional: []}});", + function_name, min_width, max_width, min_height, @@ -45,7 +50,7 @@ class WebrtcBrowserTest: public ContentBrowserTest { WebrtcBrowserTest() {} virtual ~WebrtcBrowserTest() {} - virtual void SetUpOnMainThread() OVERRIDE { + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { // We need fake devices in this test since we want to run on naked VMs. We // assume these switches are set by default in content_browsertests. ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch( @@ -53,7 +58,9 @@ class WebrtcBrowserTest: public ContentBrowserTest { ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseFakeUIForMediaStream)); - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + // The video playback will not work without a GPU, so force its use here. + // This may not be available on all VMs though. + command_line->AppendSwitch(switches::kUseGpuInTests); } protected: @@ -72,7 +79,9 @@ class WebrtcBrowserTest: public ContentBrowserTest { // see that the success callback is called. If the error callback is called or // none of the callbacks are called the tests will simply time out and fail. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetVideoStreamAndStop) { - GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/getusermedia.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("getUserMedia({video: true});")); @@ -81,7 +90,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetVideoStreamAndStop) { } IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndStop) { - GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/getusermedia.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("getUserMedia({video: true, audio: true});")); @@ -90,7 +101,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndStop) { } IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndClone) { - GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/getusermedia.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("getUserMediaAndClone();")); @@ -109,7 +122,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndClone) { // These tests will make a complete PeerConnection-based call and verify that // video is playing for the call. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupVideoCall) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("call({video: true});")); @@ -124,7 +139,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupVideoCall) { #endif IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupAudioAndVideoCall) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("call({video: true, audio: true});")); @@ -132,7 +149,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupAudioAndVideoCall) { } IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CanSetupCallAndSendDtmf) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE( @@ -140,8 +159,10 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CanSetupCallAndSendDtmf) { } IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, - CanMakeEmptyCallThenAddStreamsAndRenegotiate) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + DISABLED_CanMakeEmptyCallThenAddStreamsAndRenegotiate) { + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); const char* kJavascript = @@ -168,7 +189,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, #endif IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("callWithoutMsidAndBundle();")); @@ -178,7 +201,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, // This test will make a PeerConnection-based call and test an unreliable text // dataChannel. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataOnly) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("callWithDataOnly();")); @@ -195,7 +220,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataOnly) { // This test will make a PeerConnection-based call and test an unreliable text // dataChannel and audio and video tracks. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndMedia) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("callWithDataAndMedia();")); @@ -206,13 +233,16 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndMedia) { // Timing out on ARM linux bot: http://crbug.com/238490 #define MAYBE_CallWithDataAndLaterAddMedia DISABLED_CallWithDataAndLaterAddMedia #else -#define MAYBE_CallWithDataAndLaterAddMedia CallWithDataAndLaterAddMedia +// Temporarily disable the test on all platforms. http://crbug.com/293252 +#define MAYBE_CallWithDataAndLaterAddMedia DISABLED_CallWithDataAndLaterAddMedia #endif // This test will make a PeerConnection-based call and test an unreliable text // dataChannel and later add an audio and video track. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndLaterAddMedia) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("callWithDataAndLaterAddMedia();")); @@ -230,7 +260,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndLaterAddMedia) { // MediaStream that has been created based on a MediaStream created with // getUserMedia. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithNewVideoMediaStream) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE(ExecuteJavascript("callWithNewVideoMediaStream();")); @@ -244,7 +276,9 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithNewVideoMediaStream) { // TODO(phoglund): This test is manual since not all buildbots has an audio // input. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CallAndModifyStream) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE( @@ -254,23 +288,25 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CallAndModifyStream) { // This test calls getUserMedia in sequence with different constraints. IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TestGetUserMediaConstraints) { - GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/getusermedia.html")); std::vector<std::string> list_of_get_user_media_calls; list_of_get_user_media_calls.push_back( - GenerateGetUserMediaCall(320, 320, 180, 180, 30, 30)); + GenerateGetUserMediaCall(kGetUserMedia, 320, 320, 180, 180, 30, 30)); list_of_get_user_media_calls.push_back( - GenerateGetUserMediaCall(320, 320, 240, 240, 30, 30)); + GenerateGetUserMediaCall(kGetUserMedia, 320, 320, 240, 240, 30, 30)); list_of_get_user_media_calls.push_back( - GenerateGetUserMediaCall(640, 640, 360, 360, 30, 30)); + GenerateGetUserMediaCall(kGetUserMedia, 640, 640, 360, 360, 30, 30)); list_of_get_user_media_calls.push_back( - GenerateGetUserMediaCall(640, 640, 480, 480, 30, 30)); + GenerateGetUserMediaCall(kGetUserMedia, 640, 640, 480, 480, 30, 30)); list_of_get_user_media_calls.push_back( - GenerateGetUserMediaCall(960, 960, 720, 720, 30, 30)); + GenerateGetUserMediaCall(kGetUserMedia, 960, 960, 720, 720, 30, 30)); list_of_get_user_media_calls.push_back( - GenerateGetUserMediaCall(1280, 1280, 720, 720, 30, 30)); + GenerateGetUserMediaCall(kGetUserMedia, 1280, 1280, 720, 720, 30, 30)); list_of_get_user_media_calls.push_back( - GenerateGetUserMediaCall(1920, 1920, 1080, 1080, 30, 30)); + GenerateGetUserMediaCall(kGetUserMedia, 1920, 1920, 1080, 1080, 30, 30)); for (std::vector<std::string>::iterator const_iterator = list_of_get_user_media_calls.begin(); @@ -283,8 +319,32 @@ IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TestGetUserMediaConstraints) { } } +// This test calls getUserMedia and checks for aspect ratio behavior. +IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TestGetUserMediaAspectRatio) { + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/getusermedia.html")); + + std::string constraints_4_3 = GenerateGetUserMediaCall( + kGetUserMediaWithAnalysis, 640, 640, 480, 480, 30, 30); + std::string constraints_16_9 = GenerateGetUserMediaCall( + kGetUserMediaWithAnalysis, 640, 640, 360, 360, 30, 30); + + // TODO(mcasas): add more aspect ratios, in particular 16:10 crbug.com/275594. + + NavigateToURL(shell(), url); + EXPECT_TRUE(ExecuteJavascript(constraints_4_3)); + ExpectTitle("4:3 letterbox"); + + NavigateToURL(shell(), url); + EXPECT_TRUE(ExecuteJavascript(constraints_16_9)); + ExpectTitle("16:9 letterbox"); +} + IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, AddTwoMediaStreamsToOnePC) { - GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + ASSERT_TRUE(test_server()->Start()); + + GURL url(test_server()->GetURL("files/media/peerconnection-call.html")); NavigateToURL(shell(), url); EXPECT_TRUE( diff --git a/chromium/content/browser/media/webrtc_internals.cc b/chromium/content/browser/media/webrtc_internals.cc index f8bcdc3d321..e94447a3e0a 100644 --- a/chromium/content/browser/media/webrtc_internals.cc +++ b/chromium/content/browser/media/webrtc_internals.cc @@ -65,7 +65,7 @@ void WebRTCInternals::OnAddPeerConnection(int render_process_id, dict->SetString("url", url); peer_connection_data_.Append(dict); - if (observers_.size() > 0) + if (observers_.might_have_observers()) SendUpdate("addPeerConnection", dict); } @@ -85,7 +85,7 @@ void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) { peer_connection_data_.Remove(i, NULL); - if (observers_.size() > 0) { + if (observers_.might_have_observers()) { base::DictionaryValue id; id.SetInteger("pid", static_cast<int>(pid)); id.SetInteger("lid", lid); @@ -123,7 +123,7 @@ void WebRTCInternals::OnUpdatePeerConnection( log_entry->SetString("value", value); log->Append(log_entry); - if (observers_.size() > 0) { + if (observers_.might_have_observers()) { base::DictionaryValue update; update.SetInteger("pid", static_cast<int>(pid)); update.SetInteger("lid", lid); @@ -138,7 +138,7 @@ void WebRTCInternals::OnUpdatePeerConnection( void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid, const base::ListValue& value) { - if (observers_.size() == 0) + if (!observers_.might_have_observers()) return; base::DictionaryValue dict; @@ -165,7 +165,7 @@ void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) { } void WebRTCInternals::SendAllUpdates() { - if (observers_.size() > 0) + if (observers_.might_have_observers()) SendUpdate("updateAllPeerConnections", &peer_connection_data_); } @@ -184,7 +184,7 @@ void WebRTCInternals::StopRtpRecording() { } void WebRTCInternals::SendUpdate(const string& command, base::Value* value) { - DCHECK_GT(observers_.size(), (size_t)0); + DCHECK(observers_.might_have_observers()); FOR_EACH_OBSERVER(WebRTCInternalsUIObserver, observers_, @@ -216,7 +216,7 @@ void WebRTCInternals::OnRendererExit(int render_process_id) { record->GetInteger("rid", &this_rid); if (this_rid == render_process_id) { - if (observers_.size() > 0) { + if (observers_.might_have_observers()) { int lid = 0, pid = 0; record->GetInteger("lid", &lid); record->GetInteger("pid", &pid); diff --git a/chromium/content/browser/media/webrtc_internals_browsertest.cc b/chromium/content/browser/media/webrtc_internals_browsertest.cc index 19c25741822..b45827623b4 100644 --- a/chromium/content/browser/media/webrtc_internals_browsertest.cc +++ b/chromium/content/browser/media/webrtc_internals_browsertest.cc @@ -9,7 +9,7 @@ #include "base/values.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -118,10 +118,17 @@ class PeerConnectionEntry { static const int64 FAKE_TIME_STAMP = 3600000; -class WebRTCInternalsBrowserTest: public ContentBrowserTest { +#if defined(OS_WIN) +// All tests are flaky on Windows: crbug.com/277322. +#define MAYBE_WebRTCInternalsBrowserTest DISABLED_WebRTCInternalsBrowserTest +#else +#define MAYBE_WebRTCInternalsBrowserTest WebRTCInternalsBrowserTest +#endif + +class MAYBE_WebRTCInternalsBrowserTest: public ContentBrowserTest { public: - WebRTCInternalsBrowserTest() {} - virtual ~WebRTCInternalsBrowserTest() {} + MAYBE_WebRTCInternalsBrowserTest() {} + virtual ~MAYBE_WebRTCInternalsBrowserTest() {} virtual void SetUpOnMainThread() OVERRIDE { // We need fake devices in this test since we want to run on naked VMs. We @@ -395,7 +402,8 @@ class WebRTCInternalsBrowserTest: public ContentBrowserTest { } }; -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, AddAndRemovePeerConnection) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, + AddAndRemovePeerConnection) { GURL url("chrome://webrtc-internals"); NavigateToURL(shell(), url); @@ -416,7 +424,8 @@ IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, AddAndRemovePeerConnection) { VerifyNoElementWithId(pc_2.getIdString()); } -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, UpdateAllPeerConnections) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, + UpdateAllPeerConnections) { GURL url("chrome://webrtc-internals"); NavigateToURL(shell(), url); @@ -433,7 +442,7 @@ IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, UpdateAllPeerConnections) { VerifyPeerConnectionEntry(pc_1); } -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, UpdatePeerConnection) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, UpdatePeerConnection) { GURL url("chrome://webrtc-internals"); NavigateToURL(shell(), url); @@ -470,7 +479,7 @@ IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, UpdatePeerConnection) { } // Tests that adding random named stats updates the dataSeries and graphs. -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, AddStats) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, AddStats) { GURL url("chrome://webrtc-internals"); NavigateToURL(shell(), url); @@ -496,7 +505,7 @@ IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, AddStats) { } // Tests that the bandwidth estimation values are drawn on a single graph. -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, BweCompoundGraph) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, BweCompoundGraph) { GURL url("chrome://webrtc-internals"); NavigateToURL(shell(), url); @@ -536,7 +545,7 @@ IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, BweCompoundGraph) { // Tests that the total packet/byte count is converted to count per second, // and the converted data is drawn. -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, ConvertedGraphs) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, ConvertedGraphs) { GURL url("chrome://webrtc-internals"); NavigateToURL(shell(), url); @@ -575,16 +584,11 @@ IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, ConvertedGraphs) { } } -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) // Timing out on ARM linux bot: http://crbug.com/238490 -#define MAYBE_WithRealPeerConnectionCall DISABLED_WithRealPeerConnectionCall -#else -#define MAYBE_WithRealPeerConnectionCall WithRealPeerConnectionCall -#endif - +// Disabling due to failure on Linux, Mac, Win: http://crbug.com/272413 // Sanity check of the page content under a real PeerConnection call. -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, - MAYBE_WithRealPeerConnectionCall) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, + DISABLED_WithRealPeerConnectionCall) { // Start a peerconnection call in the first window. ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); @@ -666,7 +670,7 @@ IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, EXPECT_GT(count, 0); } -IN_PROC_BROWSER_TEST_F(WebRTCInternalsBrowserTest, CreatePageDump) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRTCInternalsBrowserTest, CreatePageDump) { GURL url("chrome://webrtc-internals"); NavigateToURL(shell(), url); diff --git a/chromium/content/browser/mime_registry_message_filter.cc b/chromium/content/browser/mime_registry_message_filter.cc index 49a050084c9..f23cc004e4b 100644 --- a/chromium/content/browser/mime_registry_message_filter.cc +++ b/chromium/content/browser/mime_registry_message_filter.cc @@ -30,8 +30,6 @@ bool MimeRegistryMessageFilter::OnMessageReceived(const IPC::Message& message, OnGetMimeTypeFromExtension) IPC_MESSAGE_HANDLER(MimeRegistryMsg_GetMimeTypeFromFile, OnGetMimeTypeFromFile) - IPC_MESSAGE_HANDLER(MimeRegistryMsg_GetPreferredExtensionForMimeType, - OnGetPreferredExtensionForMimeType) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -47,9 +45,4 @@ void MimeRegistryMessageFilter::OnGetMimeTypeFromFile( net::GetMimeTypeFromFile(file_path, mime_type); } -void MimeRegistryMessageFilter::OnGetPreferredExtensionForMimeType( - const std::string& mime_type, base::FilePath::StringType* extension) { - net::GetPreferredExtensionForMimeType(mime_type, extension); -} - } // namespace content diff --git a/chromium/content/browser/mime_registry_message_filter.h b/chromium/content/browser/mime_registry_message_filter.h index 4a719a5f2f1..e86fe190b1d 100644 --- a/chromium/content/browser/mime_registry_message_filter.h +++ b/chromium/content/browser/mime_registry_message_filter.h @@ -27,9 +27,6 @@ class MimeRegistryMessageFilter : public BrowserMessageFilter { std::string* mime_type); void OnGetMimeTypeFromFile(const base::FilePath& file_path, std::string* mime_type); - void OnGetPreferredExtensionForMimeType( - const std::string& mime_type, - base::FilePath::StringType* extension); }; } // namespace content diff --git a/chromium/content/browser/net/sqlite_persistent_cookie_store.cc b/chromium/content/browser/net/sqlite_persistent_cookie_store.cc index c23692d4750..517a8c18a16 100644 --- a/chromium/content/browser/net/sqlite_persistent_cookie_store.cc +++ b/chromium/content/browser/net/sqlite_persistent_cookie_store.cc @@ -1198,13 +1198,15 @@ net::CookieStore* CreatePersistentCookieStore( const base::FilePath& path, bool restore_old_session_cookies, quota::SpecialStoragePolicy* storage_policy, - net::CookieMonster::Delegate* cookie_monster_delegate) { + net::CookieMonster::Delegate* cookie_monster_delegate, + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner) { SQLitePersistentCookieStore* persistent_store = new SQLitePersistentCookieStore( path, BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), - BrowserThread::GetBlockingPool()->GetSequencedTaskRunner( - BrowserThread::GetBlockingPool()->GetSequenceToken()), + background_task_runner.get() ? background_task_runner : + BrowserThread::GetBlockingPool()->GetSequencedTaskRunner( + BrowserThread::GetBlockingPool()->GetSequenceToken()), restore_old_session_cookies, storage_policy); net::CookieMonster* cookie_monster = diff --git a/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc b/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc index 4f627062ec5..e71b56b7a80 100644 --- a/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc +++ b/chromium/content/browser/net/sqlite_persistent_cookie_store_perftest.cc @@ -7,10 +7,10 @@ #include "base/bind.h" #include "base/compiler_specific.h" #include "base/files/scoped_temp_dir.h" -#include "base/perftimer.h" #include "base/sequenced_task_runner.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" +#include "base/test/perf_time_logger.h" #include "base/test/sequenced_worker_pool_owner.h" #include "base/threading/sequenced_worker_pool.h" #include "net/cookies/canonical_cookie.h" @@ -118,7 +118,7 @@ class SQLitePersistentCookieStorePerfTest : public testing::Test { TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadForKeyPerformance) { for (int domain_num = 0; domain_num < 3; ++domain_num) { std::string domain_name(base::StringPrintf("domain_%d.com", domain_num)); - PerfTimeLogger timer( + base::PerfTimeLogger timer( ("Load cookies for the eTLD+1 " + domain_name).c_str()); store_->LoadCookiesForKey(domain_name, base::Bind(&SQLitePersistentCookieStorePerfTest::OnKeyLoaded, @@ -132,7 +132,7 @@ TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadForKeyPerformance) { // Test the performance of load TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadPerformance) { - PerfTimeLogger timer("Load all cookies"); + base::PerfTimeLogger timer("Load all cookies"); Load(); timer.Done(); diff --git a/chromium/content/browser/net/view_blob_internals_job_factory.cc b/chromium/content/browser/net/view_blob_internals_job_factory.cc index e877a418e54..85356e5aae5 100644 --- a/chromium/content/browser/net/view_blob_internals_job_factory.cc +++ b/chromium/content/browser/net/view_blob_internals_job_factory.cc @@ -21,9 +21,9 @@ bool ViewBlobInternalsJobFactory::IsSupportedURL(const GURL& url) { net::URLRequestJob* ViewBlobInternalsJobFactory::CreateJobForRequest( net::URLRequest* request, net::NetworkDelegate* network_delegate, - webkit_blob::BlobStorageController* blob_storage_controller) { + webkit_blob::BlobStorageContext* blob_storage_context) { return new webkit_blob::ViewBlobInternalsJob( - request, network_delegate, blob_storage_controller); + request, network_delegate, blob_storage_context); } } // namespace content diff --git a/chromium/content/browser/net/view_blob_internals_job_factory.h b/chromium/content/browser/net/view_blob_internals_job_factory.h index 0843e0651e6..4d4b60abf4c 100644 --- a/chromium/content/browser/net/view_blob_internals_job_factory.h +++ b/chromium/content/browser/net/view_blob_internals_job_factory.h @@ -11,7 +11,7 @@ class URLRequest; class URLRequestJob; } // namespace net namespace webkit_blob { -class BlobStorageController; +class BlobStorageContext; } // webkit_blob class GURL; @@ -24,7 +24,7 @@ class ViewBlobInternalsJobFactory { static net::URLRequestJob* CreateJobForRequest( net::URLRequest* request, net::NetworkDelegate* network_delegate, - webkit_blob::BlobStorageController* blob_storage_controller); + webkit_blob::BlobStorageContext* blob_storage_context); }; } // namespace content diff --git a/chromium/content/browser/notification_service_impl.cc b/chromium/content/browser/notification_service_impl.cc index 49132883f18..27de8579fe8 100644 --- a/chromium/content/browser/notification_service_impl.cc +++ b/chromium/content/browser/notification_service_impl.cc @@ -83,7 +83,7 @@ void NotificationServiceImpl::RemoveObserver(NotificationObserver* observer, observers_[type][source.map_key()]; if (observer_list) { observer_list->RemoveObserver(observer); - if (!observer_list->size()) { + if (!observer_list->might_have_observers()) { observers_[type].erase(source.map_key()); delete observer_list; } diff --git a/chromium/content/browser/plugin_browsertest.cc b/chromium/content/browser/plugin_browsertest.cc index c1dfa1f1953..37e1fa9449c 100644 --- a/chromium/content/browser/plugin_browsertest.cc +++ b/chromium/content/browser/plugin_browsertest.cc @@ -9,8 +9,8 @@ #include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" +#include "content/shell/browser/shell.h" #include "content/shell/common/shell_switches.h" -#include "content/shell/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "content/test/net/url_request_mock_http_job.h" diff --git a/chromium/content/browser/plugin_data_remover_impl_browsertest.cc b/chromium/content/browser/plugin_data_remover_impl_browsertest.cc index 37672eef947..8308a51ba51 100644 --- a/chromium/content/browser/plugin_data_remover_impl_browsertest.cc +++ b/chromium/content/browser/plugin_data_remover_impl_browsertest.cc @@ -11,7 +11,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" namespace content { diff --git a/chromium/content/browser/plugin_process_host.cc b/chromium/content/browser/plugin_process_host.cc index bffd16ef50e..86d313472e4 100644 --- a/chromium/content/browser/plugin_process_host.cc +++ b/chromium/content/browser/plugin_process_host.cc @@ -22,6 +22,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "content/browser/browser_child_process_host_impl.h" +#include "content/browser/loader/resource_message_filter.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/plugin_service_impl.h" #include "content/common/child_process_host_impl.h" @@ -31,9 +32,11 @@ #include "content/public/browser/content_browser_client.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/plugin_service.h" +#include "content/public/browser/resource_context.h" #include "content/public/common/content_switches.h" #include "content/public/common/process_type.h" #include "ipc/ipc_switches.h" +#include "net/url_request/url_request_context_getter.h" #include "ui/base/ui_base_switches.h" #include "ui/gfx/native_widget_types.h" #include "ui/gl/gl_switches.h" @@ -220,7 +223,7 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) { cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); #if defined(OS_POSIX) - base::EnvironmentVector env; + base::EnvironmentMap env; #if defined(OS_MACOSX) && !defined(__LP64__) if (!browser_command_line.HasSwitch(switches::kDisableCarbonInterposing)) { std::string interpose_list = GetContentClient()->GetCarbonInterposePath(); @@ -233,8 +236,7 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) { interpose_list.insert(0, existing_list); } } - env.push_back(std::pair<std::string, std::string>( - kDYLDInsertLibrariesKey, interpose_list)); + env[kDYLDInsertLibrariesKey] = interpose_list; } #endif #endif @@ -254,6 +256,16 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) { // been destroyed. process_->SetTerminateChildOnShutdown(false); + ResourceMessageFilter::GetContextsCallback get_contexts_callback( + base::Bind(&PluginProcessHost::GetContexts, + base::Unretained(this))); + + // TODO(jam): right now we're passing NULL for appcache, blob storage, and + // file system. If NPAPI plugins actually use this, we'll have to plumb them. + ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( + process_->GetData().id, PROCESS_TYPE_PLUGIN, NULL, NULL, NULL, + get_contexts_callback); + process_->GetHost()->AddFilter(resource_message_filter); return true; } @@ -271,6 +283,8 @@ bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PluginProcessHost, msg) IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelCreated, OnChannelCreated) + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelDestroyed, + OnChannelDestroyed) #if defined(OS_WIN) IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginWindowDestroyed, OnPluginWindowDestroyed) @@ -292,7 +306,6 @@ bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() - DCHECK(handled); return handled; } @@ -329,21 +342,6 @@ void PluginProcessHost::CancelRequests() { } } -// static -void PluginProcessHost::CancelPendingRequestsForResourceContext( - ResourceContext* context) { - for (PluginProcessHostIterator host_it; !host_it.Done(); ++host_it) { - PluginProcessHost* host = *host_it; - for (size_t i = 0; i < host->pending_requests_.size(); ++i) { - if (host->pending_requests_[i]->GetResourceContext() == context) { - host->pending_requests_[i]->OnError(); - host->pending_requests_.erase(host->pending_requests_.begin() + i); - --i; - } - } - } -} - void PluginProcessHost::OpenChannelToPlugin(Client* client) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -409,9 +407,22 @@ void PluginProcessHost::OnChannelCreated( const IPC::ChannelHandle& channel_handle) { Client* client = sent_requests_.front(); - if (client) + if (client) { + resource_context_map_[client->ID()] = client->GetResourceContext(); client->OnChannelOpened(channel_handle); + } sent_requests_.pop_front(); } +void PluginProcessHost::OnChannelDestroyed(int renderer_id) { + resource_context_map_.erase(renderer_id); +} + +void PluginProcessHost::GetContexts(const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context) { + *resource_context = resource_context_map_[request.origin_pid]; + *request_context = (*resource_context)->GetRequestContext(); +} + } // namespace content diff --git a/chromium/content/browser/plugin_process_host.h b/chromium/content/browser/plugin_process_host.h index 61ce44db221..75c667daf0f 100644 --- a/chromium/content/browser/plugin_process_host.h +++ b/chromium/content/browser/plugin_process_host.h @@ -8,6 +8,7 @@ #include "build/build_config.h" #include <list> +#include <map> #include <set> #include <string> #include <vector> @@ -22,6 +23,9 @@ #include "content/public/common/webplugininfo.h" #include "ipc/ipc_channel_proxy.h" #include "ui/gfx/native_widget_types.h" +#include "webkit/common/resource_type.h" + +struct ResourceHostMsg_Request; namespace gfx { class Rect; @@ -31,6 +35,10 @@ namespace IPC { struct ChannelHandle; } +namespace net { +class URLRequestContext; +} + namespace content { class BrowserChildProcessHostImpl; class ResourceContext; @@ -87,9 +95,6 @@ class CONTENT_EXPORT PluginProcessHost : public BrowserChildProcessHostDelegate, // OnChannelOpened in the client is called. void OpenChannelToPlugin(Client* client); - // Cancels all pending channel requests for the given resource context. - static void CancelPendingRequestsForResourceContext(ResourceContext* context); - // This function is called to cancel pending requests to open new channels. void CancelPendingRequest(Client* client); @@ -125,6 +130,7 @@ class CONTENT_EXPORT PluginProcessHost : public BrowserChildProcessHostDelegate, // Message handlers. void OnChannelCreated(const IPC::ChannelHandle& channel_handle); + void OnChannelDestroyed(int renderer_id); #if defined(OS_WIN) void OnPluginWindowDestroyed(HWND window, HWND parent); @@ -148,6 +154,11 @@ class CONTENT_EXPORT PluginProcessHost : public BrowserChildProcessHostDelegate, void CancelRequests(); + // Callback for ResourceMessageFilter. + void GetContexts(const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context); + // These are channel requests that we are waiting to send to the // plugin process once the channel is opened. std::vector<Client*> pending_requests_; @@ -174,6 +185,10 @@ class CONTENT_EXPORT PluginProcessHost : public BrowserChildProcessHostDelegate, bool plugin_cursor_visible_; #endif + // Map from render_process_id to its ResourceContext + typedef std::map<int, ResourceContext*> ResourceContextMap; + ResourceContextMap resource_context_map_; + scoped_ptr<BrowserChildProcessHostImpl> process_; DISALLOW_COPY_AND_ASSIGN(PluginProcessHost); diff --git a/chromium/content/browser/plugin_service_impl.cc b/chromium/content/browser/plugin_service_impl.cc index 6682a4d4604..b46af4fbeff 100644 --- a/chromium/content/browser/plugin_service_impl.cc +++ b/chromium/content/browser/plugin_service_impl.cc @@ -33,7 +33,7 @@ #if defined(OS_WIN) #include "content/common/plugin_constants_win.h" -#include "ui/base/win/hwnd_util.h" +#include "ui/gfx/win/hwnd_util.h" #endif #if defined(OS_POSIX) @@ -328,8 +328,7 @@ PluginProcessHost* PluginServiceImpl::FindOrStartNpapiPluginProcess( PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess( int render_process_id, const base::FilePath& plugin_path, - const base::FilePath& profile_data_directory, - PpapiPluginProcessHost::PluginClient* client) { + const base::FilePath& profile_data_directory) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) @@ -356,8 +355,7 @@ PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess( // This plugin isn't loaded by any plugin process, so create a new process. return PpapiPluginProcessHost::CreatePluginHost( - *info, profile_data_directory, - client->GetResourceContext()->GetHostResolver()); + *info, profile_data_directory); } PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess( @@ -413,7 +411,7 @@ void PluginServiceImpl::OpenChannelToPpapiPlugin( const base::FilePath& profile_data_directory, PpapiPluginProcessHost::PluginClient* client) { PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess( - render_process_id, plugin_path, profile_data_directory, client); + render_process_id, plugin_path, profile_data_directory); if (plugin_host) { plugin_host->OpenChannelToPlugin(client); } else { @@ -660,7 +658,7 @@ void PluginServiceImpl::RegisterPepperPlugins() { PepperPluginInfo* PluginServiceImpl::GetRegisteredPpapiPluginInfo( const base::FilePath& plugin_path) { PepperPluginInfo* info = NULL; - for (size_t i = 0; i < ppapi_plugins_.size(); i++) { + for (size_t i = 0; i < ppapi_plugins_.size(); ++i) { if (ppapi_plugins_[i].path == plugin_path) { info = &ppapi_plugins_[i]; break; @@ -790,7 +788,8 @@ void PluginServiceImpl::GetInternalPlugins( } bool PluginServiceImpl::NPAPIPluginsSupported() { -#if defined(OS_WIN) || defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(USE_AURA)) +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_BSD) || \ + (defined(OS_LINUX) && !defined(USE_AURA)) return true; #else return false; @@ -839,7 +838,7 @@ bool PluginServiceImpl::GetPluginInfoFromWindow( } bool PluginServiceImpl::IsPluginWindow(HWND window) { - return ui::GetClassName(window) == base::string16(kNativeWindowClassName); + return gfx::GetClassName(window) == base::string16(kNativeWindowClassName); } #endif diff --git a/chromium/content/browser/plugin_service_impl.h b/chromium/content/browser/plugin_service_impl.h index 6d358fd21a9..11fb257e99b 100644 --- a/chromium/content/browser/plugin_service_impl.h +++ b/chromium/content/browser/plugin_service_impl.h @@ -131,8 +131,7 @@ class CONTENT_EXPORT PluginServiceImpl PpapiPluginProcessHost* FindOrStartPpapiPluginProcess( int render_process_id, const base::FilePath& plugin_path, - const base::FilePath& profile_data_directory, - PpapiPluginProcessHost::PluginClient* client); + const base::FilePath& profile_data_directory); PpapiPluginProcessHost* FindOrStartPpapiBrokerProcess( int render_process_id, const base::FilePath& plugin_path); diff --git a/chromium/content/browser/plugin_service_impl_browsertest.cc b/chromium/content/browser/plugin_service_impl_browsertest.cc index 1abf725e435..e99f3e537c6 100644 --- a/chromium/content/browser/plugin_service_impl_browsertest.cc +++ b/chromium/content/browser/plugin_service_impl_browsertest.cc @@ -15,7 +15,7 @@ #include "content/public/common/content_switches.h" #include "content/public/test/test_browser_thread.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "testing/gmock/include/gmock/gmock.h" diff --git a/chromium/content/browser/power_monitor_message_broadcaster.cc b/chromium/content/browser/power_monitor_message_broadcaster.cc index 77abcb959f9..23de161b05c 100644 --- a/chromium/content/browser/power_monitor_message_broadcaster.cc +++ b/chromium/content/browser/power_monitor_message_broadcaster.cc @@ -36,4 +36,4 @@ void PowerMonitorMessageBroadcaster::OnResume() { sender_->Send(new PowerMonitorMsg_Resume()); } -} // namespace content
\ No newline at end of file +} // namespace content diff --git a/chromium/content/browser/power_monitor_message_broadcaster.h b/chromium/content/browser/power_monitor_message_broadcaster.h index f0e3a207e2d..04af601d719 100644 --- a/chromium/content/browser/power_monitor_message_broadcaster.h +++ b/chromium/content/browser/power_monitor_message_broadcaster.h @@ -38,4 +38,4 @@ class CONTENT_EXPORT PowerMonitorMessageBroadcaster } // namespace base -#endif // CONTENT_BROWSER_POWER_MONITOR_MESSAGE_BROADCASTER_H_
\ No newline at end of file +#endif // CONTENT_BROWSER_POWER_MONITOR_MESSAGE_BROADCASTER_H_ diff --git a/chromium/content/browser/power_save_blocker_chromeos.cc b/chromium/content/browser/power_save_blocker_chromeos.cc index 9eda5c45027..f9a6722f8d2 100644 --- a/chromium/content/browser/power_save_blocker_chromeos.cc +++ b/chromium/content/browser/power_save_blocker_chromeos.cc @@ -27,10 +27,8 @@ class PowerSaveBlockerImpl::Delegate void ApplyBlock() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!chromeos::DBusThreadManager::IsInitialized()) { - LOG(WARNING) << "DBusThreadManager not initialized"; + if (!chromeos::DBusThreadManager::IsInitialized()) return; - } chromeos::PowerPolicyController* controller = chromeos::DBusThreadManager::Get()->GetPowerPolicyController(); @@ -48,10 +46,9 @@ class PowerSaveBlockerImpl::Delegate void RemoveBlock() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!chromeos::DBusThreadManager::IsInitialized()) { - LOG(WARNING) << "DBusThreadManager not initialized"; + if (!chromeos::DBusThreadManager::IsInitialized()) return; - } + chromeos::DBusThreadManager::Get()->GetPowerPolicyController()-> RemoveWakeLock(block_id_); } diff --git a/chromium/content/browser/power_save_blocker_x11.cc b/chromium/content/browser/power_save_blocker_x11.cc index d955643f281..f55fac9580a 100644 --- a/chromium/content/browser/power_save_blocker_x11.cc +++ b/chromium/content/browser/power_save_blocker_x11.cc @@ -29,11 +29,12 @@ #include "dbus/message.h" #include "dbus/object_path.h" #include "dbus/object_proxy.h" +#include "ui/gfx/x/x11_types.h" #if defined(TOOLKIT_GTK) #include "base/message_loop/message_pump_gtk.h" #else -#include "base/message_loop/message_pump_aurax11.h" +#include "base/message_loop/message_pump_x11.h" #endif namespace { @@ -298,7 +299,7 @@ void PowerSaveBlockerImpl::Delegate::RemoveBlock(DBusAPI api) { // static bool PowerSaveBlockerImpl::Delegate::DPMSEnabled() { - Display* display = base::MessagePumpForUI::GetDefaultXDisplay(); + XDisplay* display = base::MessagePumpForUI::GetDefaultXDisplay(); BOOL enabled = false; int dummy; if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) { diff --git a/chromium/content/browser/ppapi_plugin_process_host.cc b/chromium/content/browser/ppapi_plugin_process_host.cc index c9aa6994660..ecbbfd6bfce 100644 --- a/chromium/content/browser/ppapi_plugin_process_host.cc +++ b/chromium/content/browser/ppapi_plugin_process_host.cc @@ -114,10 +114,9 @@ PpapiPluginProcessHost::~PpapiPluginProcessHost() { // static PpapiPluginProcessHost* PpapiPluginProcessHost::CreatePluginHost( const PepperPluginInfo& info, - const base::FilePath& profile_data_directory, - net::HostResolver* host_resolver) { + const base::FilePath& profile_data_directory) { PpapiPluginProcessHost* plugin_host = new PpapiPluginProcessHost( - info, profile_data_directory, host_resolver); + info, profile_data_directory); if (plugin_host->Init(info)) return plugin_host; @@ -206,8 +205,7 @@ void PpapiPluginProcessHost::OpenChannelToPlugin(Client* client) { PpapiPluginProcessHost::PpapiPluginProcessHost( const PepperPluginInfo& info, - const base::FilePath& profile_data_directory, - net::HostResolver* host_resolver) + const base::FilePath& profile_data_directory) : permissions_( ppapi::PpapiPermissions::GetForCommandLine(info.permissions)), profile_data_directory_(profile_data_directory), @@ -215,13 +213,11 @@ PpapiPluginProcessHost::PpapiPluginProcessHost( process_.reset(new BrowserChildProcessHostImpl( PROCESS_TYPE_PPAPI_PLUGIN, this)); - filter_ = new PepperMessageFilter(permissions_, host_resolver); - host_impl_.reset(new BrowserPpapiHostImpl(this, permissions_, info.name, info.path, profile_data_directory, - false, - filter_)); + false)); + filter_ = new PepperMessageFilter(); process_->GetHost()->AddFilter(filter_.get()); process_->GetHost()->AddFilter(host_impl_->message_filter().get()); @@ -243,8 +239,7 @@ PpapiPluginProcessHost::PpapiPluginProcessHost() host_impl_.reset(new BrowserPpapiHostImpl(this, permissions, std::string(), base::FilePath(), base::FilePath(), - false, - NULL)); + false)); } bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) { @@ -333,7 +328,7 @@ bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) { new PpapiPluginSandboxedProcessLauncherDelegate(is_broker_), #elif defined(OS_POSIX) use_zygote, - base::EnvironmentVector(), + base::EnvironmentMap(), #endif cmd_line); return true; diff --git a/chromium/content/browser/ppapi_plugin_process_host.h b/chromium/content/browser/ppapi_plugin_process_host.h index 78d6bd63c50..87c79c31d2a 100644 --- a/chromium/content/browser/ppapi_plugin_process_host.h +++ b/chromium/content/browser/ppapi_plugin_process_host.h @@ -21,10 +21,6 @@ #include "ipc/ipc_sender.h" #include "ppapi/shared_impl/ppapi_permissions.h" -namespace net { -class HostResolver; -} - namespace content { class BrowserChildProcessHostImpl; class ResourceContext; @@ -76,8 +72,7 @@ class PpapiPluginProcessHost : public BrowserChildProcessHostDelegate, static PpapiPluginProcessHost* CreatePluginHost( const PepperPluginInfo& info, - const base::FilePath& profile_data_directory, - net::HostResolver* host_resolver); + const base::FilePath& profile_data_directory); static PpapiPluginProcessHost* CreateBrokerHost( const PepperPluginInfo& info); @@ -122,8 +117,7 @@ class PpapiPluginProcessHost : public BrowserChildProcessHostDelegate, // Constructors for plugin and broker process hosts, respectively. // You must call Init before doing anything else. PpapiPluginProcessHost(const PepperPluginInfo& info, - const base::FilePath& profile_data_directory, - net::HostResolver* host_resolver); + const base::FilePath& profile_data_directory); PpapiPluginProcessHost(); // Actually launches the process with the given plugin info. Returns true diff --git a/chromium/content/browser/renderer_host/DEPS b/chromium/content/browser/renderer_host/DEPS index 34decdb0223..0848d364726 100644 --- a/chromium/content/browser/renderer_host/DEPS +++ b/chromium/content/browser/renderer_host/DEPS @@ -5,10 +5,6 @@ include_rules = [ "+third_party/zlib", "+third_party/libyuv", - # For single-process mode. - "+content/renderer/render_process_impl.h", - "+content/renderer/render_thread_impl.h", - # The renderer_host files should only call upwards in the layering via the # delegate interfaces. "-content/browser/web_contents", diff --git a/chromium/content/browser/renderer_host/backing_store_gtk.cc b/chromium/content/browser/renderer_host/backing_store_gtk.cc index 62365bed873..2eed33ace2f 100644 --- a/chromium/content/browser/renderer_host/backing_store_gtk.cc +++ b/chromium/content/browser/renderer_host/backing_store_gtk.cc @@ -29,10 +29,10 @@ #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/gtk/gtk_signal.h" -#include "ui/base/x/x11_util.h" #include "ui/base/x/x11_util_internal.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" +#include "ui/gfx/x/x11_types.h" #include "ui/surface/transport_dib.h" namespace content { @@ -59,7 +59,7 @@ static const int kMaxVideoLayerSize = 23170; // Destroys the image and the associated shared memory structures. This is a // helper function for code using shared memory. -void DestroySharedImage(Display* display, +void DestroySharedImage(XDisplay* display, XImage* image, XShmSegmentInfo* shminfo) { XShmDetach(display, shminfo); @@ -72,7 +72,7 @@ void DestroySharedImage(Display* display, // XSyncExtension to push a callback into the X11 event queue and get a // callback instead of blocking until the event queue is cleared. // -// TODO(erg): If ui::GetXDisplay() ever gets fixed to handle multiple Displays, +// TODO(erg): If gfx::GetXDisplay() ever gets fixed to handle multiple Displays, // this must be modified to be per Display instead of a Singleton. class XSyncHandler { public: @@ -85,7 +85,7 @@ class XSyncHandler { } void PushPaintCounter(TransportDIB* dib, - Display* display, + XDisplay* display, Picture picture, Pixmap pixmap, const base::Closure& completion_callback); @@ -96,7 +96,7 @@ class XSyncHandler { // A struct that has cleanup and callback tasks that were queued into the // future and are run on |g_backing_store_sync_alarm| firing. struct BackingStoreEvents { - BackingStoreEvents(TransportDIB* dib, Display* d, Picture pic, Pixmap pix, + BackingStoreEvents(TransportDIB* dib, XDisplay* d, Picture pic, Pixmap pix, const base::Closure& c) : dib(dib), display(d), @@ -109,7 +109,7 @@ class XSyncHandler { TransportDIB* dib; // The display we're running on. - Display* display; + XDisplay* display; // Data to delete. Picture picture; @@ -142,7 +142,7 @@ class XSyncHandler { }; void XSyncHandler::PushPaintCounter(TransportDIB* dib, - Display* display, + XDisplay* display, Picture picture, Pixmap pixmap, const base::Closure& completion_callback) { @@ -153,7 +153,7 @@ void XSyncHandler::PushPaintCounter(TransportDIB* dib, // alarm when it is processed. XSyncValue value; XSyncIntToValue(&value, 1); - XSyncChangeCounter(ui::GetXDisplay(), + XSyncChangeCounter(gfx::GetXDisplay(), backing_store_sync_counter_, value); } @@ -164,7 +164,7 @@ XSyncHandler::XSyncHandler() xsync_error_base_(0), backing_store_sync_counter_(0), backing_store_sync_alarm_(0) { - Display* display = ui::GetXDisplay(); + XDisplay* display = gfx::GetXDisplay(); if (XSyncQueryExtension(display, &xsync_event_base_, &xsync_error_base_)) { @@ -191,7 +191,7 @@ XSyncHandler::~XSyncHandler() { if (loaded_extension_) gdk_window_remove_filter(NULL, &OnEventThunk, this); - XSync(ui::GetXDisplay(), False); + XSync(gfx::GetXDisplay(), False); while (!backing_store_events_.empty()) { // We delete the X11 resources we're holding onto. We don't run the // callbacks because we are shutting down. @@ -248,7 +248,7 @@ BackingStoreGtk::BackingStoreGtk(RenderWidgetHost* widget, void* visual, int depth) : BackingStore(widget, size), - display_(ui::GetXDisplay()), + display_(gfx::GetXDisplay()), shared_memory_support_(ui::QuerySharedMemorySupport(display_)), use_render_(ui::QueryRenderSupport(display_)), visual_(visual), diff --git a/chromium/content/browser/renderer_host/backing_store_gtk.h b/chromium/content/browser/renderer_host/backing_store_gtk.h index 0e81597c0b6..29776fcdf67 100644 --- a/chromium/content/browser/renderer_host/backing_store_gtk.h +++ b/chromium/content/browser/renderer_host/backing_store_gtk.h @@ -12,7 +12,7 @@ #include "build/build_config.h" #include "content/browser/renderer_host/backing_store.h" #include "content/common/content_export.h" -#include "ui/base/x/x11_util.h" +#include "ui/gfx/x/x11_types.h" namespace gfx { class Point; @@ -39,7 +39,7 @@ class CONTENT_EXPORT BackingStoreGtk : public BackingStore { virtual ~BackingStoreGtk(); - Display* display() const { return display_; } + XDisplay* display() const { return display_; } XID root_window() const { return root_window_; } // Copy from the server-side backing store to the target window @@ -79,7 +79,7 @@ class CONTENT_EXPORT BackingStoreGtk : public BackingStore { // This is the connection to the X server where this backing store will be // displayed. - Display* const display_; + XDisplay* const display_; // What flavor, if any, MIT-SHM (X shared memory) support we have. const ui::SharedMemorySupport shared_memory_support_; // If this is true, then we can use Xrender to composite our pixmaps. diff --git a/chromium/content/browser/renderer_host/backing_store_win.cc b/chromium/content/browser/renderer_host/backing_store_win.cc index 5ccafbb062b..8e761dd0c32 100644 --- a/chromium/content/browser/renderer_host/backing_store_win.cc +++ b/chromium/content/browser/renderer_host/backing_store_win.cc @@ -9,10 +9,10 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/public/common/content_switches.h" #include "skia/ext/platform_canvas.h" -#include "ui/base/win/dpi.h" #include "ui/gfx/gdi_util.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/size_conversions.h" +#include "ui/gfx/win/dpi.h" #include "ui/surface/transport_dib.h" namespace content { @@ -92,7 +92,7 @@ bool BackingStoreWin::ColorManagementEnabled() { size_t BackingStoreWin::MemorySize() { gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize(size(), - ui::win::GetDeviceScaleFactor())); + gfx::win::GetDeviceScaleFactor())); return size_in_pixels.GetArea() * (color_depth_ / 8); } @@ -104,6 +104,7 @@ void BackingStoreWin::PaintToBackingStore( float scale_factor, const base::Closure& completion_callback, bool* scheduled_completion_callback) { + TRACE_EVENT0("content", "BackingStoreWin::PaintToBackingStore"); *scheduled_completion_callback = false; gfx::Size size_in_pixels = gfx::ToCeiledSize(gfx::ScaleSize( size(), scale_factor)); @@ -155,6 +156,7 @@ void BackingStoreWin::PaintToBackingStore( bool BackingStoreWin::CopyFromBackingStore(const gfx::Rect& rect, skia::PlatformBitmap* output) { + TRACE_EVENT0("content", "BackingStoreWin::CopyFromBackingStore"); // TODO(kevers): Make sure this works with HiDPI backing stores. if (!output->Allocate(rect.width(), rect.height(), true)) return false; @@ -168,10 +170,11 @@ bool BackingStoreWin::CopyFromBackingStore(const gfx::Rect& rect, void BackingStoreWin::ScrollBackingStore(const gfx::Vector2d& delta, const gfx::Rect& clip_rect, const gfx::Size& view_size) { + TRACE_EVENT0("content", "BackingStoreWin::ScrollBackingStore"); // TODO(darin): this doesn't work if delta x() and y() are both non-zero! DCHECK(delta.x() == 0 || delta.y() == 0); - float scale = ui::win::GetDeviceScaleFactor(); + float scale = gfx::win::GetDeviceScaleFactor(); gfx::Rect screen_rect = gfx::ToEnclosingRect( gfx::ScaleRect(clip_rect, scale)); int dx = static_cast<int>(delta.x() * scale); diff --git a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc index 1bcc8223e97..e4563375681 100644 --- a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc +++ b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc @@ -26,9 +26,9 @@ bool BasicMouseWheelSmoothScrollGesture::ForwardInputEvents( if (pixels_scrolled_ >= pixels_to_scroll_) return false; - double position_delta = smooth_scroll_calculator_.GetScrollDelta( + float position_delta = synthetic_gesture_calculator_.GetDelta( now, - RenderWidgetHostImpl::From(host)->GetSyntheticScrollMessageInterval()); + RenderWidgetHostImpl::From(host)->GetSyntheticGestureMessageInterval()); WebKit::WebMouseWheelEvent event; diff --git a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h index adea7a30b74..ba127da05bd 100644 --- a/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h +++ b/chromium/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h @@ -6,14 +6,14 @@ #define CONTENT_BROWSER_RENDERER_HOST_BASIC_MOUSE_WHEEL_SMOOTH_SCROLL_GESTURE_ #include "base/time/time.h" -#include "content/browser/renderer_host/smooth_scroll_calculator.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/browser/renderer_host/synthetic_gesture_calculator.h" +#include "content/port/browser/synthetic_gesture.h" namespace content { class RenderWidgetHost; -class BasicMouseWheelSmoothScrollGesture : public SmoothScrollGesture { +class BasicMouseWheelSmoothScrollGesture : public SyntheticGesture { public: BasicMouseWheelSmoothScrollGesture(bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y); @@ -23,7 +23,7 @@ class BasicMouseWheelSmoothScrollGesture : public SmoothScrollGesture { private: virtual ~BasicMouseWheelSmoothScrollGesture(); - SmoothScrollCalculator smooth_scroll_calculator_; + SyntheticGestureCalculator synthetic_gesture_calculator_; bool scroll_down_; int pixels_scrolled_; diff --git a/chromium/content/browser/renderer_host/clipboard_message_filter.cc b/chromium/content/browser/renderer_host/clipboard_message_filter.cc index 0e2426bf224..e681188b937 100644 --- a/chromium/content/browser/renderer_host/clipboard_message_filter.cc +++ b/chromium/content/browser/renderer_host/clipboard_message_filter.cc @@ -11,7 +11,6 @@ #include "content/public/browser/browser_context.h" #include "ipc/ipc_message_macros.h" #include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/zlib/zlib.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/size.h" #include "url/gurl.h" @@ -199,19 +198,9 @@ void ClipboardMessageFilter::OnReadImageReply( const SkBitmap& bitmap, IPC::Message* reply_msg) { base::SharedMemoryHandle image_handle = base::SharedMemory::NULLHandle(); uint32 image_size = 0; - std::string reply_data; if (!bitmap.isNull()) { std::vector<unsigned char> png_data; - SkAutoLockPixels lock(bitmap); - if (gfx::PNGCodec::EncodeWithCompressionLevel( - static_cast<const unsigned char*>(bitmap.getPixels()), - gfx::PNGCodec::FORMAT_BGRA, - gfx::Size(bitmap.width(), bitmap.height()), - bitmap.rowBytes(), - false, - std::vector<gfx::PNGCodec::Comment>(), - Z_BEST_SPEED, - &png_data)) { + if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &png_data)) { base::SharedMemory buffer; if (buffer.CreateAndMapAnonymous(png_data.size())) { memcpy(buffer.memory(), vector_as_array(&png_data), png_data.size()); diff --git a/chromium/content/browser/renderer_host/compositing_iosurface_mac.h b/chromium/content/browser/renderer_host/compositing_iosurface_mac.h index 674e4e512d6..623272be771 100644 --- a/chromium/content/browser/renderer_host/compositing_iosurface_mac.h +++ b/chromium/content/browser/renderer_host/compositing_iosurface_mac.h @@ -19,7 +19,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "media/base/video_frame.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" diff --git a/chromium/content/browser/renderer_host/compositor_impl_android.cc b/chromium/content/browser/renderer_host/compositor_impl_android.cc index 53c42e4277a..1e758c35a0d 100644 --- a/chromium/content/browser/renderer_host/compositor_impl_android.cc +++ b/chromium/content/browser/renderer_host/compositor_impl_android.cc @@ -22,10 +22,13 @@ #include "cc/output/compositor_frame.h" #include "cc/output/context_provider.h" #include "cc/output/output_surface.h" +#include "cc/resources/scoped_ui_resource.h" +#include "cc/resources/ui_resource_bitmap.h" #include "cc/trees/layer_tree_host.h" #include "content/browser/gpu/browser_gpu_channel_host_factory.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/common/gpu/client/command_buffer_proxy_impl.h" +#include "content/common/gpu/client/context_provider_command_buffer.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/client/gpu_channel_host.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" @@ -37,6 +40,7 @@ #include "third_party/khronos/GLES2/gl2ext.h" #include "ui/gfx/android/device_display_info.h" #include "ui/gfx/android/java_bitmap.h" +#include "webkit/common/gpu/context_provider_in_process.h" #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" namespace gfx { @@ -48,8 +52,9 @@ namespace { // Used for drawing directly to the screen. Bypasses resizing and swaps. class DirectOutputSurface : public cc::OutputSurface { public: - DirectOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : cc::OutputSurface(context3d.Pass()) { + DirectOutputSurface( + const scoped_refptr<cc::ContextProvider>& context_provider) + : cc::OutputSurface(context_provider) { capabilities_.adjust_deadline_for_parent = false; } @@ -57,23 +62,26 @@ class DirectOutputSurface : public cc::OutputSurface { surface_size_ = size; } virtual void SwapBuffers(cc::CompositorFrame*) OVERRIDE { - context3d()->shallowFlushCHROMIUM(); + context_provider_->Context3d()->shallowFlushCHROMIUM(); } }; // Used to override capabilities_.adjust_deadline_for_parent to false class OutputSurfaceWithoutParent : public cc::OutputSurface { public: - OutputSurfaceWithoutParent(scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : cc::OutputSurface(context3d.Pass()) { + OutputSurfaceWithoutParent( + const scoped_refptr< + content::ContextProviderCommandBuffer>& context_provider) + : cc::OutputSurface(context_provider) { capabilities_.adjust_deadline_for_parent = false; } virtual void SwapBuffers(cc::CompositorFrame* frame) OVERRIDE { - content::WebGraphicsContext3DCommandBufferImpl* command_buffer = - static_cast<content::WebGraphicsContext3DCommandBufferImpl*>(context3d()); + content::WebGraphicsContext3DCommandBufferImpl* command_buffer_context = + static_cast<content::WebGraphicsContext3DCommandBufferImpl*>( + context_provider_->Context3d()); content::CommandBufferProxyImpl* command_buffer_proxy = - command_buffer->GetCommandBufferProxy(); + command_buffer_context->GetCommandBufferProxy(); DCHECK(command_buffer_proxy); command_buffer_proxy->SetLatencyInfo(frame->metadata.latency_info); @@ -227,10 +235,11 @@ void CompositorImpl::SetSurface(jobject surface) { void CompositorImpl::SetVisible(bool visible) { if (!visible) { + ui_resource_map_.clear(); host_.reset(); + client_->UIResourcesAreInvalid(); } else if (!host_) { cc::LayerTreeSettings settings; - settings.compositor_name = "BrowserCompositor"; settings.refresh_rate = 60.0; settings.impl_side_painting = false; settings.allow_antialiasing = false; @@ -255,6 +264,9 @@ void CompositorImpl::SetVisible(bool visible) { host_->SetLayerTreeHostClientReady(); host_->SetViewportSize(size_); host_->set_has_transparent_background(has_transparent_background_); + // Need to recreate the UI resources because a new LayerTreeHost has been + // created. + client_->DidLoseUIResources(); } } @@ -286,6 +298,23 @@ bool CompositorImpl::CompositeAndReadback(void *pixels, const gfx::Rect& rect) { return false; } +cc::UIResourceId CompositorImpl::GenerateUIResource( + const cc::UIResourceBitmap& bitmap) { + if (!host_) + return 0; + scoped_ptr<cc::ScopedUIResource> ui_resource = + cc::ScopedUIResource::Create(host_.get(), bitmap); + cc::UIResourceId id = ui_resource->id(); + ui_resource_map_.set(id, ui_resource.Pass()); + return id; +} + +void CompositorImpl::DeleteUIResource(cc::UIResourceId resource_id) { + UIResourceMap::iterator it = ui_resource_map_.find(resource_id); + if (it != ui_resource_map_.end()) + ui_resource_map_.erase(it); +} + WebKit::WebGLId CompositorImpl::GenerateTexture(gfx::JavaBitmap& bitmap) { unsigned int texture_id = BuildBasicTexture(); WebKit::WebGraphicsContext3D* context = @@ -358,6 +387,41 @@ bool CompositorImpl::CopyTextureToBitmap(WebKit::WebGLId texture_id, return true; } +static scoped_ptr<WebGraphicsContext3DCommandBufferImpl> +CreateGpuProcessViewContext( + const WebKit::WebGraphicsContext3D::Attributes attributes, + int surface_id, + base::WeakPtr<CompositorImpl> compositor_impl) { + GpuChannelHostFactory* factory = BrowserGpuChannelHostFactory::instance(); + GURL url("chrome://gpu/Compositor::createContext3D"); + scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context( + new WebGraphicsContext3DCommandBufferImpl(surface_id, + url, + factory, + compositor_impl)); + static const size_t kBytesPerPixel = 4; + gfx::DeviceDisplayInfo display_info; + size_t full_screen_texture_size_in_bytes = + display_info.GetDisplayHeight() * + display_info.GetDisplayWidth() * + kBytesPerPixel; + if (!context->Initialize( + attributes, + false, + CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE, + 64 * 1024, // command buffer size + 64 * 1024, // start transfer buffer size + 64 * 1024, // min transfer buffer size + std::min(3 * full_screen_texture_size_in_bytes, + kDefaultMaxTransferBufferSize), + 2 * 1024 * 1024 // mapped memory limit + )) { + LOG(ERROR) << "Failed to create 3D context for compositor."; + return scoped_ptr<WebGraphicsContext3DCommandBufferImpl>(); + } + return context.Pass(); +} + scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface( bool fallback) { WebKit::WebGraphicsContext3D::Attributes attrs; @@ -365,46 +429,35 @@ scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface( attrs.noAutomaticFlushes = true; if (g_use_direct_gl) { - scoped_ptr<WebKit::WebGraphicsContext3D> context( - webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl:: - CreateViewContext(attrs, window_)); - if (!window_) { - return scoped_ptr<cc::OutputSurface>( - new DirectOutputSurface(context.Pass())); - } + using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; + scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl> context3d = + WebGraphicsContext3DInProcessCommandBufferImpl::CreateViewContext( + attrs, window_); + scoped_refptr<webkit::gpu::ContextProviderInProcess> context_provider = + webkit::gpu::ContextProviderInProcess::Create(context3d.Pass(), + "BrowserCompositor"); + + scoped_ptr<cc::OutputSurface> output_surface; + if (!window_) + output_surface.reset(new DirectOutputSurface(context_provider)); + else + output_surface.reset(new cc::OutputSurface(context_provider)); + return output_surface.Pass(); + } - return make_scoped_ptr(new cc::OutputSurface(context.Pass())); - } else { - DCHECK(window_ && surface_id_); - GpuChannelHostFactory* factory = BrowserGpuChannelHostFactory::instance(); - GURL url("chrome://gpu/Compositor::createContext3D"); - scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context( - new WebGraphicsContext3DCommandBufferImpl(surface_id_, - url, - factory, - weak_factory_.GetWeakPtr())); - static const size_t kBytesPerPixel = 4; - gfx::DeviceDisplayInfo display_info; - size_t full_screen_texture_size_in_bytes = - display_info.GetDisplayHeight() * - display_info.GetDisplayWidth() * - kBytesPerPixel; - if (!context->Initialize( - attrs, - false, - CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE, - 64 * 1024, // command buffer size - 64 * 1024, // start transfer buffer size - 64 * 1024, // min transfer buffer size - std::min(3 * full_screen_texture_size_in_bytes, - kDefaultMaxTransferBufferSize))) { - LOG(ERROR) << "Failed to create 3D context for compositor."; - return scoped_ptr<cc::OutputSurface>(); - } - return scoped_ptr<cc::OutputSurface>( - new OutputSurfaceWithoutParent( - context.PassAs<WebKit::WebGraphicsContext3D>())); + DCHECK(window_); + DCHECK(surface_id_); + + scoped_refptr<ContextProviderCommandBuffer> context_provider = + ContextProviderCommandBuffer::Create(CreateGpuProcessViewContext( + attrs, surface_id_, weak_factory_.GetWeakPtr()), "BrowserCompositor"); + if (!context_provider.get()) { + LOG(ERROR) << "Failed to create 3D context for compositor."; + return scoped_ptr<cc::OutputSurface>(); } + + return scoped_ptr<cc::OutputSurface>( + new OutputSurfaceWithoutParent(context_provider)); } void CompositorImpl::OnLostResources() { diff --git a/chromium/content/browser/renderer_host/compositor_impl_android.h b/chromium/content/browser/renderer_host/compositor_impl_android.h index 845e830aad4..58e44c89449 100644 --- a/chromium/content/browser/renderer_host/compositor_impl_android.h +++ b/chromium/content/browser/renderer_host/compositor_impl_android.h @@ -7,8 +7,10 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "cc/resources/ui_resource_client.h" #include "cc/trees/layer_tree_host_client.h" #include "content/browser/renderer_host/image_transport_factory_android.h" #include "content/common/content_export.h" @@ -21,6 +23,7 @@ namespace cc { class InputHandlerClient; class Layer; class LayerTreeHost; +class ScopedUIResource; } namespace content { @@ -60,6 +63,9 @@ class CONTENT_EXPORT CompositorImpl void *pixels, const gfx::Rect& rect) OVERRIDE; virtual void SetNeedsRedraw() OVERRIDE; virtual void Composite() OVERRIDE; + virtual cc::UIResourceId GenerateUIResource( + const cc::UIResourceBitmap& bitmap) OVERRIDE; + virtual void DeleteUIResource(cc::UIResourceId resource_id) OVERRIDE; virtual WebKit::WebGLId GenerateTexture(gfx::JavaBitmap& bitmap) OVERRIDE; virtual WebKit::WebGLId GenerateCompressedTexture( gfx::Size& size, int data_size, void* data) OVERRIDE; @@ -117,6 +123,10 @@ class CONTENT_EXPORT CompositorImpl scoped_refptr<cc::ContextProvider> null_offscreen_context_provider_; + typedef base::ScopedPtrHashMap<cc::UIResourceId, cc::ScopedUIResource> + UIResourceMap; + UIResourceMap ui_resource_map_; + DISALLOW_COPY_AND_ASSIGN(CompositorImpl); }; diff --git a/chromium/content/browser/renderer_host/frame_memory_manager.cc b/chromium/content/browser/renderer_host/frame_memory_manager.cc new file mode 100644 index 00000000000..708bddfa61b --- /dev/null +++ b/chromium/content/browser/renderer_host/frame_memory_manager.cc @@ -0,0 +1,66 @@ +// Copyright 2013 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/browser/renderer_host/frame_memory_manager.h" + +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/sys_info.h" + +namespace content { + +namespace { + +size_t MaxNumberOfSavedFrames() { + return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256)); +} + +} // namespace + +FrameMemoryManager* FrameMemoryManager::GetInstance() { + return Singleton<FrameMemoryManager>::get(); +} + +void FrameMemoryManager::AddFrame(FrameContainer* frame, bool visible) { + RemoveFrame(frame); + if (visible) + visible_frames_.insert(frame); + else + hidden_frames_.push_front(frame); + CullHiddenFrames(); +} + +void FrameMemoryManager::RemoveFrame(FrameContainer* frame) { + visible_frames_.erase(frame); + hidden_frames_.remove(frame); +} + +void FrameMemoryManager::SetFrameVisibility(FrameContainer* frame, + bool visible) { + if (visible) { + hidden_frames_.remove(frame); + visible_frames_.insert(frame); + } else { + visible_frames_.erase(frame); + hidden_frames_.push_front(frame); + CullHiddenFrames(); + } +} + +FrameMemoryManager::FrameMemoryManager() {} + +FrameMemoryManager::~FrameMemoryManager() {} + +void FrameMemoryManager::CullHiddenFrames() { + while (!hidden_frames_.empty() && + hidden_frames_.size() + visible_frames_.size() > + MaxNumberOfSavedFrames()) { + size_t old_size = hidden_frames_.size(); + // Should remove self from list. + hidden_frames_.back()->ReleaseCurrentFrame(); + DCHECK_EQ(hidden_frames_.size() + 1, old_size); + } +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/frame_memory_manager.h b/chromium/content/browser/renderer_host/frame_memory_manager.h new file mode 100644 index 00000000000..ce3fc258226 --- /dev/null +++ b/chromium/content/browser/renderer_host/frame_memory_manager.h @@ -0,0 +1,44 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ + +#include <list> +#include <set> + +#include "base/basictypes.h" + +template <typename T> struct DefaultSingletonTraits; + +namespace content { + +class FrameContainer { + public: + virtual void ReleaseCurrentFrame() = 0; +}; + +class FrameMemoryManager { + public: + static FrameMemoryManager* GetInstance(); + + void AddFrame(FrameContainer*, bool visible); + void RemoveFrame(FrameContainer*); + void SetFrameVisibility(FrameContainer*, bool visible); + + private: + FrameMemoryManager(); + ~FrameMemoryManager(); + void CullHiddenFrames(); + friend struct DefaultSingletonTraits<FrameMemoryManager>; + + std::set<FrameContainer*> visible_frames_; + std::list<FrameContainer*> hidden_frames_; + + DISALLOW_COPY_AND_ASSIGN(FrameMemoryManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_FRAME_MEMORY_MANAGER_H_ diff --git a/chromium/content/browser/renderer_host/generic_touch_gesture_android.cc b/chromium/content/browser/renderer_host/generic_touch_gesture_android.cc new file mode 100644 index 00000000000..74080cd408c --- /dev/null +++ b/chromium/content/browser/renderer_host/generic_touch_gesture_android.cc @@ -0,0 +1,62 @@ +// Copyright 2013 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/browser/renderer_host/generic_touch_gesture_android.h" + +#include "base/debug/trace_event.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "jni/GenericTouchGesture_jni.h" + +namespace { +bool g_jni_initialized = false; + +void RegisterNativesIfNeeded(JNIEnv* env) { + if (!g_jni_initialized) { + content::RegisterNativesImpl(env); + g_jni_initialized = true; + } +} +} // namespace + +namespace content { + +GenericTouchGestureAndroid::GenericTouchGestureAndroid( + RenderWidgetHost* rwh, + base::android::ScopedJavaLocalRef<jobject> java_touch_gesture) + : has_started_(false), + has_finished_(false), + rwh_(rwh), + java_touch_gesture_(java_touch_gesture) { + JNIEnv* env = base::android::AttachCurrentThread(); + RegisterNativesIfNeeded(env); +} + +GenericTouchGestureAndroid::~GenericTouchGestureAndroid() { +} + +bool GenericTouchGestureAndroid::ForwardInputEvents( + base::TimeTicks now, RenderWidgetHost* host) { + if (!has_started_) { + has_started_ = true; + JNIEnv* env = base::android::AttachCurrentThread(); + Java_GenericTouchGesture_start( + env, java_touch_gesture_.obj(), reinterpret_cast<intptr_t>(this)); + } + + return !has_finished_; +} + +float GenericTouchGestureAndroid::GetDelta( + JNIEnv* env, jobject obj, float scale) { + return synthetic_gesture_calculator_.GetDelta( + base::TimeTicks::Now(), + RenderWidgetHostImpl::From(rwh_)->GetSyntheticGestureMessageInterval()) * + scale; +} + +void GenericTouchGestureAndroid::SetHasFinished(JNIEnv* env, jobject obj) { + has_finished_ = true; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/generic_touch_gesture_android.h b/chromium/content/browser/renderer_host/generic_touch_gesture_android.h new file mode 100644 index 00000000000..030239b1c30 --- /dev/null +++ b/chromium/content/browser/renderer_host/generic_touch_gesture_android.h @@ -0,0 +1,48 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_ +#define CONTENT_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_ + +#include "base/android/jni_android.h" +#include "base/time/time.h" +#include "content/browser/renderer_host/synthetic_gesture_calculator.h" +#include "content/port/browser/synthetic_gesture.h" + +namespace content { + +class ContentViewCore; +class RenderWidgetHost; + +class GenericTouchGestureAndroid : public SyntheticGesture { + public: + GenericTouchGestureAndroid( + RenderWidgetHost* rwh, + base::android::ScopedJavaLocalRef<jobject> java_scroller); + + // Called by the java side once the TimeAnimator ticks. + float GetDelta(JNIEnv* env, jobject obj, float scale); + void SetHasFinished(JNIEnv* env, jobject obj); + + // SmoothScrollGesture + virtual bool ForwardInputEvents(base::TimeTicks now, + RenderWidgetHost* host) OVERRIDE; + + private: + virtual ~GenericTouchGestureAndroid(); + + SyntheticGestureCalculator synthetic_gesture_calculator_; + + bool has_started_; + bool has_finished_; + + RenderWidgetHost* rwh_; + base::android::ScopedJavaGlobalRef<jobject> java_touch_gesture_; + + DISALLOW_COPY_AND_ASSIGN(GenericTouchGestureAndroid); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_ diff --git a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc b/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc index 55d2e76bb30..65dc54524c2 100644 --- a/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc +++ b/chromium/content/browser/renderer_host/gtk_im_context_wrapper.cc @@ -439,7 +439,7 @@ void GtkIMContextWrapper::ProcessInputMethodResult(const GdkEventKey* event, // Unlike a Char event, an IME event is NOT dispatched to onkeypress() // handlers or autofill. host->ImeConfirmComposition( - commit_text_,ui::Range::InvalidRange(),false); + commit_text_,gfx::Range::InvalidRange(),false); // Set this flag to false, as this composition session has been // finished. is_composing_text_ = false; @@ -477,7 +477,7 @@ void GtkIMContextWrapper::ConfirmComposition() { if (host_view_->GetRenderWidgetHost()) { RenderWidgetHostImpl::From( host_view_->GetRenderWidgetHost())->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); } // Reset the input method. @@ -503,7 +503,7 @@ void GtkIMContextWrapper::HandleCommit(const string16& text) { SendFakeCompositionKeyEvent(WebKit::WebInputEvent::RawKeyDown); RenderWidgetHostImpl::From( host_view_->GetRenderWidgetHost())->ImeConfirmComposition( - text, ui::Range::InvalidRange(), false); + text, gfx::Range::InvalidRange(), false); SendFakeCompositionKeyEvent(WebKit::WebInputEvent::KeyUp); } } @@ -532,7 +532,7 @@ void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, // TODO(suzhe): due to a bug of webkit, we currently can't use selection range // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805 - composition_.selection = ui::Range(cursor_position); + composition_.selection = gfx::Range(cursor_position); // In case we are using a buggy input method which doesn't fire // "preedit_start" signal. diff --git a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc b/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc index 93e20778607..861491111b5 100644 --- a/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc +++ b/chromium/content/browser/renderer_host/gtk_plugin_container_manager.cc @@ -9,7 +9,7 @@ #include "base/logging.h" #include "content/browser/renderer_host/gtk_plugin_container.h" #include "content/common/webplugin_geometry.h" -#include "ui/base/gtk/gtk_compat.h" +#include "ui/gfx/gtk_compat.h" #include "ui/gfx/gtk_util.h" namespace content { diff --git a/chromium/content/browser/renderer_host/gtk_window_utils.cc b/chromium/content/browser/renderer_host/gtk_window_utils.cc index 979c58d05a5..f05cdca3b66 100644 --- a/chromium/content/browser/renderer_host/gtk_window_utils.cc +++ b/chromium/content/browser/renderer_host/gtk_window_utils.cc @@ -10,10 +10,11 @@ #include <vector> -#include "ui/base/gtk/gtk_compat.h" +#include "third_party/WebKit/public/web/WebScreenInfo.h" #include "ui/base/x/x11_util.h" +#include "ui/gfx/gtk_compat.h" #include "ui/gfx/rect.h" -#include "third_party/WebKit/public/web/WebScreenInfo.h" +#include "ui/gfx/x/x11_types.h" namespace content { @@ -44,7 +45,7 @@ gfx::Rect GetWorkArea(Window window) { property[start_index + 2], property[start_index + 3]); } -WebKit::WebScreenInfo GetScreenInfo(Display* display, int screenNumber) { +WebKit::WebScreenInfo GetScreenInfo(XDisplay* display, int screenNumber) { // XDisplayWidth() and XDisplayHeight() return cached values. To ensure that // we return the correct dimensions after the screen is resized, query the // root window's geometry each time. diff --git a/chromium/content/browser/renderer_host/image_transport_factory_android.cc b/chromium/content/browser/renderer_host/image_transport_factory_android.cc index 39071c9bec3..bc0d365d249 100644 --- a/chromium/content/browser/renderer_host/image_transport_factory_android.cc +++ b/chromium/content/browser/renderer_host/image_transport_factory_android.cc @@ -128,7 +128,9 @@ CmdBufferImageTransportFactory::CmdBufferImageTransportFactory() { 64 * 1024, // starting buffer size 64 * 1024, // min buffer size std::min(3 * full_screen_texture_size_in_bytes, - kDefaultMaxTransferBufferSize)); + kDefaultMaxTransferBufferSize), + WebGraphicsContext3DCommandBufferImpl::kNoLimit + ); if (context_->makeContextCurrent()) context_->pushGroupMarkerEXT( diff --git a/chromium/content/browser/renderer_host/ime_adapter_android.cc b/chromium/content/browser/renderer_host/ime_adapter_android.cc index 588c5ada8e6..da14d7a3d5d 100644 --- a/chromium/content/browser/renderer_host/ime_adapter_android.cc +++ b/chromium/content/browser/renderer_host/ime_adapter_android.cc @@ -174,7 +174,7 @@ void ImeAdapterAndroid::CommitText(JNIEnv* env, jobject, jstring text) { return; string16 text16 = ConvertJavaStringToUTF16(env, text); - rwhi->ImeConfirmComposition(text16, ui::Range::InvalidRange(), false); + rwhi->ImeConfirmComposition(text16, gfx::Range::InvalidRange(), false); } void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) { @@ -182,7 +182,7 @@ void ImeAdapterAndroid::FinishComposingText(JNIEnv* env, jobject) { if (!rwhi) return; - rwhi->ImeConfirmComposition(string16(), ui::Range::InvalidRange(), true); + rwhi->ImeConfirmComposition(string16(), gfx::Range::InvalidRange(), true); } void ImeAdapterAndroid::AttachImeAdapter(JNIEnv* env, jobject java_object) { diff --git a/chromium/content/browser/renderer_host/input/OWNERS b/chromium/content/browser/renderer_host/input/OWNERS index 4dabaf0da2c..4893edbdf02 100644 --- a/chromium/content/browser/renderer_host/input/OWNERS +++ b/chromium/content/browser/renderer_host/input/OWNERS @@ -1,2 +1,3 @@ -nduca@chromium.org aelias@chromium.org +jdduke@chromium.org +nduca@chromium.org diff --git a/chromium/content/browser/renderer_host/input/browser_input_event.cc b/chromium/content/browser/renderer_host/input/browser_input_event.cc new file mode 100644 index 00000000000..276245e3526 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/browser_input_event.cc @@ -0,0 +1,47 @@ +// Copyright 2013 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/browser/renderer_host/input/browser_input_event.h" + +#include "content/common/input/web_input_event_payload.h" + +namespace content { + +BrowserInputEvent::BrowserInputEvent(BrowserInputEventClient* client) + : client_(client) {} + +BrowserInputEvent::~BrowserInputEvent() {} + +scoped_ptr<BrowserInputEvent> BrowserInputEvent::Create( + int64 id, + scoped_ptr<InputEvent::Payload> payload, + BrowserInputEventClient* client) { + DCHECK(client); + scoped_ptr<BrowserInputEvent> event(new BrowserInputEvent(client)); + event->Initialize(id, payload.Pass()); + return event.Pass(); +} + +void BrowserInputEvent::OnDispatched( + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup_events) { + DCHECK(followup_events); + + bool event_consumed = + disposition == INPUT_EVENT_MAIN_THREAD_CONSUMED || + disposition == INPUT_EVENT_IMPL_THREAD_CONSUMED || + disposition == INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED; + + if (!event_consumed && CanCreateFollowupEvents()) + client_->OnDispatched(*this, disposition, followup_events); + else + client_->OnDispatched(*this, disposition); +} + +bool BrowserInputEvent::CanCreateFollowupEvents() const { + return payload()->GetType() == InputEvent::Payload::WEB_INPUT_EVENT && + WebInputEventPayload::Cast(payload())->CanCreateFollowupEvents(); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/browser_input_event.h b/chromium/content/browser/renderer_host/input/browser_input_event.h new file mode 100644 index 00000000000..6022d80ecd2 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/browser_input_event.h @@ -0,0 +1,69 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_vector.h" +#include "content/common/input/input_event.h" +#include "content/common/input/input_event_disposition.h" + +namespace content { + +class BrowserInputEvent; + +// Provides customized dispatch response for BrowserInputEvents. +class BrowserInputEventClient { + public: + virtual ~BrowserInputEventClient() {} + + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) {} + + // Called if the event went unconsumed and can create followup events. Any + // events added to |followup| by the client will be inserted into the + // current input event stream. |followup| will never be NULL. + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) {} +}; + +// Augmented InputEvent allowing customized dispatch response in the browser. +class CONTENT_EXPORT BrowserInputEvent : public InputEvent { + public: + // |client| is assumed to be non-NULL. + static scoped_ptr<BrowserInputEvent> Create( + int64 id, + scoped_ptr<InputEvent::Payload> payload, + BrowserInputEventClient* client); + + template <typename PayloadType> + static scoped_ptr<BrowserInputEvent> Create(int64 id, + scoped_ptr<PayloadType> payload, + BrowserInputEventClient* client) { + return Create(id, payload.template PassAs<InputEvent::Payload>(), client); + } + + virtual ~BrowserInputEvent(); + + // |followup_events| must not be NULL, and will only be modified if the + // event went unconsumed and can create followup events. + void OnDispatched(InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup_events); + + protected: + explicit BrowserInputEvent(BrowserInputEventClient* client); + + bool CanCreateFollowupEvents() const; + + private: + BrowserInputEventClient* client_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_ diff --git a/chromium/content/browser/renderer_host/input/buffered_input_router.cc b/chromium/content/browser/renderer_host/input/buffered_input_router.cc new file mode 100644 index 00000000000..97b565311d5 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/buffered_input_router.cc @@ -0,0 +1,336 @@ +// Copyright 2013 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/browser/renderer_host/input/buffered_input_router.h" + +#include "base/auto_reset.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" +#include "content/browser/renderer_host/input/input_queue.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/common/input_messages.h" +#include "content/common/view_messages.h" +#include "content/public/browser/native_web_keyboard_event.h" +#include "content/public/browser/user_metrics.h" + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebKeyboardEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; + +namespace content { + +BufferedInputRouter::BufferedInputRouter(IPC::Sender* sender, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id) + : client_(client), + ack_handler_(ack_handler), + sender_(sender), + routing_id_(routing_id), + has_touch_handler_(false), + queued_touch_count_(0), + input_queue_override_(NULL), + next_input_id_(1), + in_flight_packet_id_(0) { + input_queue_.reset(new InputQueue(this)); +} + +BufferedInputRouter::~BufferedInputRouter() {} + +void BufferedInputRouter::Flush() { + TRACE_EVENT0("input", "BufferedInputRouter::Flush"); + DCHECK_EQ(0, in_flight_packet_id_); + input_queue_->BeginFlush(); +} + +bool BufferedInputRouter::SendInput(scoped_ptr<IPC::Message> message) { + DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); + DCHECK(message->type() != InputMsg_HandleEventPacket::ID); + DCHECK(message->type() != InputMsg_HandleInputEvent::ID); + input_queue_->QueueEvent(BrowserInputEvent::Create( + NextInputID(), IPCInputEventPayload::Create(message.Pass()), this)); + return true; +} + +void BufferedInputRouter::SendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) { + if (!client_->OnSendMouseEvent(mouse_event)) + return; + // TODO(jdduke): Coalescing, http://crbug.com/289520 + QueueWebEvent(mouse_event.event, mouse_event.latency, false); +} + +void BufferedInputRouter::SendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) { + if (!client_->OnSendWheelEvent(wheel_event)) + return; + QueueWebEvent(wheel_event.event, wheel_event.latency, false); +} + +void BufferedInputRouter::SendKeyboardEvent( + const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info) { + bool is_shortcut = false; + if (!client_->OnSendKeyboardEvent(key_event, latency_info, &is_shortcut)) + return; + int64 event_id = QueueWebEvent(key_event, latency_info, is_shortcut); + if (event_id) { + DCHECK(queued_key_map_.find(event_id) == queued_key_map_.end()); + queued_key_map_[event_id] = key_event; + } +} + +void BufferedInputRouter::SendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) { + if (!client_->OnSendGestureEvent(gesture_event)) + return; + QueueWebEvent(gesture_event.event, gesture_event.latency, false); +} + +void BufferedInputRouter::SendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) { + if (!client_->OnSendTouchEvent(touch_event)) + return; + if (QueueWebEvent(touch_event.event, touch_event.latency, false)) + ++queued_touch_count_; +} + +void BufferedInputRouter::SendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) { + if (!client_->OnSendMouseEventImmediately(mouse_event)) + return; + QueueWebEvent(mouse_event.event, mouse_event.latency, false); +} + +void BufferedInputRouter::SendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) { + if (!client_->OnSendTouchEventImmediately(touch_event)) + return; + QueueWebEvent(touch_event.event, touch_event.latency, false); +} + +void BufferedInputRouter::SendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) { + if (!client_->OnSendGestureEventImmediately(gesture_event)) + return; + QueueWebEvent(gesture_event.event, gesture_event.latency, false); +} + +void BufferedInputRouter::Deliver(const EventPacket& packet) { + TRACE_EVENT2("input", "BufferedInputRouter::DeliverPacket", + "id", packet.id(), + "events", packet.size()); + DCHECK(packet.id()); + DCHECK(!in_flight_packet_id_); + if (!sender_->Send(new InputMsg_HandleEventPacket( + routing_id_, packet, InputEventDispositions()))) { + return; + } + + in_flight_packet_id_ = packet.id(); + client_->IncrementInFlightEventCount(); +} + +void BufferedInputRouter::DidFinishFlush() { + TRACE_EVENT0("input", "BufferedInputRouter::DidFinishFlush"); + client_->DidFlush(); +} + +void BufferedInputRouter::SetNeedsFlush() { + TRACE_EVENT0("input", "BufferedInputRouter::SetNeedsFlush"); + client_->SetNeedsFlush(); +} + +void BufferedInputRouter::OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) { + // Only WebInputEvents currently have ack response. + if (event.payload()->GetType() != InputEvent::Payload::WEB_INPUT_EVENT) + return; + + const WebInputEventPayload* web_payload = + WebInputEventPayload::Cast(event.payload()); + + OnWebInputEventAck(event.id(), + *web_payload->web_event(), + web_payload->latency_info(), + ToAckState(disposition), + true); +} + +void BufferedInputRouter::OnDispatched( + const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) { + DCHECK(followup); + DCHECK_NE(INPUT_EVENT_ACK_STATE_CONSUMED, ToAckState(disposition)); + + // Events sent to the router within this scope will be added to |followup|. + base::AutoReset<ScopedVector<BrowserInputEvent>*> input_queue_override( + &input_queue_override_, followup); + + OnDispatched(event, disposition); +} + +bool BufferedInputRouter::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + bool message_is_ok = true; + IPC_BEGIN_MESSAGE_MAP_EX(BufferedInputRouter, message, message_is_ok) + IPC_MESSAGE_HANDLER(InputHostMsg_HandleEventPacket_ACK, OnEventPacketAck) + IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, + OnHasTouchEventHandlers) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + if (!message_is_ok) + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); + + return handled; +} + +const NativeWebKeyboardEvent* + BufferedInputRouter::GetLastKeyboardEvent() const { + return queued_key_map_.empty() ? NULL : &queued_key_map_.begin()->second; +} + +bool BufferedInputRouter::ShouldForwardTouchEvent() const { + return has_touch_handler_ && queued_touch_count_ > 0; +} + +bool BufferedInputRouter::ShouldForwardGestureEvent( + const GestureEventWithLatencyInfo& touch_event) const { + return true; +} + +void BufferedInputRouter::OnWebInputEventAck( + int64 event_id, + const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + InputEventAckState acked_result, + bool ack_from_input_queue) { + if (WebInputEvent::isKeyboardEventType(web_event.type)) { + if (ack_from_input_queue) { + KeyMap::iterator key_it = queued_key_map_.find(event_id); + DCHECK(key_it != queued_key_map_.end()); + NativeWebKeyboardEvent key_event = key_it->second; + queued_key_map_.erase(key_it); + ack_handler_->OnKeyboardEventAck(key_event, acked_result); + } else { + DCHECK_EQ(0, event_id); + ack_handler_->OnKeyboardEventAck( + static_cast<const NativeWebKeyboardEvent&>(web_event), acked_result); + } + // WARNING: This BufferedInputRouter can be deallocated at this point + // (i.e. in the case of Ctrl+W, where the call to + // HandleKeyboardEvent destroys this BufferedInputRouter). + } else if (web_event.type == WebInputEvent::MouseWheel) { + ack_handler_->OnWheelEventAck( + static_cast<const WebMouseWheelEvent&>(web_event), acked_result); + } else if (WebInputEvent::isTouchEventType(web_event.type)) { + if (ack_from_input_queue) { + DCHECK_GT(queued_touch_count_, 0); + --queued_touch_count_; + } + ack_handler_->OnTouchEventAck( + TouchEventWithLatencyInfo(static_cast<const WebTouchEvent&>(web_event), + latency_info), + acked_result); + } else if (WebInputEvent::isGestureEventType(web_event.type)) { + ack_handler_->OnGestureEventAck( + static_cast<const WebGestureEvent&>(web_event), acked_result); + } else + NOTREACHED() << "Unexpected WebInputEvent in OnWebInputEventAck"; +} + +void BufferedInputRouter::OnEventPacketAck( + int64 packet_id, + const InputEventDispositions& dispositions) { + TRACE_EVENT2("input", "BufferedInputRouter::OnEventPacketAck", + "id", packet_id, + "dispositions", dispositions.size()); + if (!in_flight_packet_id_ || packet_id != in_flight_packet_id_) { + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); + return; + } + + in_flight_packet_id_ = 0; + client_->DecrementInFlightEventCount(); + + InputQueue::AckResult ack_result = + input_queue_->OnEventPacketAck(packet_id, dispositions); + if (ack_result == InputQueue::ACK_UNEXPECTED) + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); + else if (ack_result == InputQueue::ACK_INVALID) + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); +} + +void BufferedInputRouter::OnHasTouchEventHandlers(bool has_handlers) { + if (has_touch_handler_ == has_handlers) + return; + has_touch_handler_ = has_handlers; + client_->OnHasTouchEventHandlers(has_handlers); +} + +int64 BufferedInputRouter::QueueWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + bool is_key_shortcut) { + TRACE_EVENT0("input", "BufferedInputRouter::QueueWebEvent"); + + if (FilterWebEvent(web_event, latency_info)) { + TRACE_EVENT_INSTANT0("input", + "BufferedInputRouter::QueueWebEvent::Filtered", + TRACE_EVENT_SCOPE_THREAD); + return 0; + } + + int64 event_id = NextInputID(); + scoped_ptr<BrowserInputEvent> event = BrowserInputEvent::Create( + event_id, + WebInputEventPayload::Create(web_event, + latency_info, + is_key_shortcut), + this); + + // The presence of |input_queue_override_| implies that we are in the + // scope of |OnInputEventDispatched()| with an event can create followup. + if (input_queue_override_) + input_queue_override_->push_back(event.release()); + else + input_queue_->QueueEvent(event.Pass()); + + return event_id; +} + +bool BufferedInputRouter::FilterWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info) { + // Perform optional, synchronous event handling, sending ACK messages for + // processed events, or proceeding as usual. + InputEventAckState filter_ack = + client_->FilterInputEvent(web_event, latency_info); + switch (filter_ack) { + // Send the ACK and early exit. + case INPUT_EVENT_ACK_STATE_CONSUMED: + case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: + OnWebInputEventAck(0, web_event, latency_info, filter_ack, false); + // WARNING: |this| may be deleted at this point. + return true; + + // Drop the event. + case INPUT_EVENT_ACK_STATE_UNKNOWN: + return true; + + // Proceed as normal. + case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: + default: + break; + }; + + return false; +} + +int64 BufferedInputRouter::NextInputID() { return next_input_id_++; } + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/buffered_input_router.h b/chromium/content/browser/renderer_host/input/buffered_input_router.h new file mode 100644 index 00000000000..a9802a68198 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/buffered_input_router.h @@ -0,0 +1,144 @@ +// Copyright 2013 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_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_ +#define CONTENT_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_queue.h" +#include "content/browser/renderer_host/input/input_queue_client.h" +#include "content/browser/renderer_host/input/input_router.h" + +namespace IPC { +class Sender; +} + +namespace content { + +class InputAckHandler; +class RenderProcessHost; +class RenderWidgetHostImpl; + +// Batches input events into EventPackets using a general input queue. Packets +// are sent the renderer on |Flush()|, called in response to flush requests. +class CONTENT_EXPORT BufferedInputRouter + : public NON_EXPORTED_BASE(BrowserInputEventClient), + public NON_EXPORTED_BASE(InputQueueClient), + public NON_EXPORTED_BASE(InputRouter) { + public: + // |sender|, |client| and |ack_handler| must outlive the BufferedInputRouter. + BufferedInputRouter(IPC::Sender* sender, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id); + virtual ~BufferedInputRouter(); + + // InputRouter + virtual void Flush() OVERRIDE; + virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE; + + // Certain unhandled input event acks may create follow-up events, e.g., + // TouchEvent -> GestureEvent. If these follow-up events are sent to the + // router synchronously from the original event's |OnDispatched()| ack, they + // will be inserted into the current input flush stream. + virtual void SendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual void SendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE; + virtual void SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info) OVERRIDE; + virtual void SendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual void SendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual void SendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual void SendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual void SendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual const NativeWebKeyboardEvent* GetLastKeyboardEvent() const OVERRIDE; + virtual bool ShouldForwardTouchEvent() const OVERRIDE; + virtual bool ShouldForwardGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) const OVERRIDE; + + // InputQueueClient + virtual void Deliver(const EventPacket& packet) OVERRIDE; + virtual void DidFinishFlush() OVERRIDE; + virtual void SetNeedsFlush() OVERRIDE; + + // BrowserInputEventClient + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) OVERRIDE; + // Events delivered to the router within the scope of + // |OnDispatched()| will be added to |followup|. + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) OVERRIDE; + + // IPC::Receiver + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + protected: + void OnWebInputEventAck(int64 event_id, + const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + InputEventAckState acked_result, + bool ack_from_input_queue); + void OnEventPacketAck(int64 packet_id, + const InputEventDispositions& dispositions); + void OnHasTouchEventHandlers(bool has_handlers); + + // Returns the non-zero ID associated with the |InputEvent| added to the + // |input_queue_|. If the event was dropped or filtered, returns 0. + int64 QueueWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info, + bool is_key_shortcut); + // Used by |QueueWebEvent()|; returns true if an event was filtered and should + // not be added to the |input_queue_|. + bool FilterWebEvent(const WebKit::WebInputEvent& web_event, + const ui::LatencyInfo& latency_info); + + // Generates a monotonically increasing sequences of id's, starting with 1. + int64 NextInputID(); + + const InputQueue* input_queue() const { return input_queue_.get(); } + + private: + InputRouterClient* client_; + InputAckHandler* ack_handler_; + IPC::Sender* sender_; + int routing_id_; + + scoped_ptr<InputQueue> input_queue_; + + // TODO(jdduke): Remove when we can properly serialize NativeWebKeyboardEvent. + // Alternatively, attach WebInputEvents to InputEvents but don't serialize. + typedef std::map<int64, NativeWebKeyboardEvent> KeyMap; + KeyMap queued_key_map_; + + // Necessary for |ShouldForwardTouchEvent()|. + bool has_touch_handler_; + int queued_touch_count_; + + // This is non-NULL ONLY in the scope of OnInputEventAck(event, injector). + ScopedVector<BrowserInputEvent>* input_queue_override_; + + // Used to assign unique ID's to each InputEvent that is generated. + int64 next_input_id_; + + // 0 if there no in-flight EventPacket. + int64 in_flight_packet_id_; + + DISALLOW_COPY_AND_ASSIGN(BufferedInputRouter); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_ diff --git a/chromium/content/browser/renderer_host/input/buffered_input_router_unittest.cc b/chromium/content/browser/renderer_host/input/buffered_input_router_unittest.cc new file mode 100644 index 00000000000..4d63444fef8 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/buffered_input_router_unittest.cc @@ -0,0 +1,320 @@ +// Copyright 2013 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 "base/basictypes.h" +#include "content/browser/renderer_host/input/buffered_input_router.h" +#include "content/browser/renderer_host/input/input_router_unittest.h" +#include "content/common/input/event_packet.h" +#include "content/common/input_messages.h" +#include "content/common/view_messages.h" +#include "testing/gtest/include/gtest/gtest.h" + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +class TestBufferedInputRouter : public BufferedInputRouter { + public: + TestBufferedInputRouter(IPC::Sender* sender, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id) + : BufferedInputRouter(sender, client, ack_handler, routing_id) {} + + + size_t QueuedEventCount() const { return input_queue()->QueuedEventCount(); } +}; + +class BufferedInputRouterTest : public InputRouterTest { + public: + BufferedInputRouterTest() {} + virtual ~BufferedInputRouterTest() {} + + protected: + // InputRouterTest + virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* handler, + int routing_id) OVERRIDE { + return scoped_ptr<InputRouter>( + new TestBufferedInputRouter(process, client, handler, routing_id)); + } + + bool FinishFlush(const InputEventDispositions& dispositions) { + if (!process_->sink().message_count()) + return false; + IPC::Message message(*process_->sink().GetMessageAt(0)); + process_->sink().ClearMessages(); + + InputMsg_HandleEventPacket::Param param; + InputMsg_HandleEventPacket::Read(&message, ¶m); + EventPacket& packet = param.a; + + return SendEventPacketACK(packet.id(), dispositions); + } + + bool FinishFlush(InputEventDisposition disposition) { + if (!process_->sink().message_count()) + return false; + IPC::Message message(*process_->sink().GetMessageAt(0)); + process_->sink().ClearMessages(); + + InputMsg_HandleEventPacket::Param param; + InputMsg_HandleEventPacket::Read(&message, ¶m); + EventPacket& packet = param.a; + + return SendEventPacketACK( + packet.id(), InputEventDispositions(packet.size(), disposition)); + } + + bool SendEventPacketACK(int id, const InputEventDispositions& dispositions) { + return input_router_->OnMessageReceived( + InputHostMsg_HandleEventPacket_ACK(0, id, dispositions)); + } + + size_t QueuedEventCount() const { + return buffered_input_router()->QueuedEventCount(); + } + + TestBufferedInputRouter* buffered_input_router() const { + return static_cast<TestBufferedInputRouter*>(input_router_.get()); + } +}; + +TEST_F(BufferedInputRouterTest, InputEventsProperlyQueued) { + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Redo(MSG_ROUTING_NONE)))); + EXPECT_EQ(1U, QueuedEventCount()); + + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Cut(MSG_ROUTING_NONE)))); + EXPECT_EQ(2U, QueuedEventCount()); + + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Copy(MSG_ROUTING_NONE)))); + EXPECT_EQ(3U, QueuedEventCount()); + + EXPECT_TRUE(input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_Paste(MSG_ROUTING_NONE)))); + EXPECT_EQ(4U, QueuedEventCount()); +} + +#define SCOPED_EXPECT(CALL, MESSAGE) { SCOPED_TRACE(MESSAGE); CALL; } + +TEST_F(BufferedInputRouterTest, ClientOnSendEventCalled) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(1U, QueuedEventCount()); + + SimulateWheelEvent(5, 0, 0, false); + EXPECT_EQ(2U, QueuedEventCount()); + + SimulateMouseMove(5, 0, 0); + EXPECT_EQ(3U, QueuedEventCount()); + + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + WebGestureEvent::Touchpad); + EXPECT_EQ(4U, QueuedEventCount()); + + SimulateTouchEvent(1, 1); + EXPECT_EQ(5U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, ClientOnSendEventHonored) { + client_->set_allow_send_event(false); + + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateWheelEvent(5, 0, 0, false); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateMouseMove(5, 0, 0); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + WebGestureEvent::Touchpad); + EXPECT_EQ(0U, QueuedEventCount()); + + SimulateTouchEvent(1, 1); + EXPECT_EQ(0U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, FlightCountIncrementedOnDeliver) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(0, client_->in_flight_event_count()); + + input_router_->Flush(); + EXPECT_EQ(1, client_->in_flight_event_count()); +} + +TEST_F(BufferedInputRouterTest, FlightCountDecrementedOnAck) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(0, client_->in_flight_event_count()); + + input_router_->Flush(); + EXPECT_EQ(1, client_->in_flight_event_count()); + + // The in-flight count should continue until the flush has finished. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER)); + EXPECT_EQ(1, client_->in_flight_event_count()); + + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(0, client_->in_flight_event_count()); +} + +TEST_F(BufferedInputRouterTest, FilteredEventsNeverQueued) { + // Event should not be queued, but should be ack'ed. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_CONSUMED); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled"); + EXPECT_EQ(0U, QueuedEventCount()); + ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + // Event should not be queued, but should be ack'ed. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled"); + EXPECT_EQ(0U, QueuedEventCount()); + ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + // |INPUT_EVENT_DISPOSITION_UNKNOWN| should drop the event without ack'ing. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_UNKNOWN); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled"); + EXPECT_EQ(0U, QueuedEventCount()); + ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + // Event should be queued. + client_->set_filter_state(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled"); + EXPECT_EQ(1U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, FollowupEventsInjected) { + // Enable a followup gesture event. + WebGestureEvent followup_event; + followup_event.type = WebInputEvent::GestureScrollBegin; + followup_event.data.scrollUpdate.deltaX = 10; + ack_handler_->set_followup_touch_event(make_scoped_ptr( + new GestureEventWithLatencyInfo(followup_event, ui::LatencyInfo()))); + + // Create an initial packet of { Touch, Key } and start flushing. + SimulateTouchEvent(1, 1); + EXPECT_EQ(1U, QueuedEventCount()); + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(2U, QueuedEventCount()); + input_router_->Flush(); + + // Followup only triggered when event handled. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER)); + SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled"); + EXPECT_EQ(2U, QueuedEventCount()); + + // Ack the touch event. + InputEventDispositions dispositions; + dispositions.push_back(INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED); + dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); + ASSERT_TRUE(FinishFlush(dispositions)); + + // Ack'ing the touch event should have inserted the followup gesture event; + // the flush is not complete until the inserted event is ack'ed. + SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled"); + SCOPED_EXPECT(client_->ExpectSendCalled(true), "SendGestureCalled"); + EXPECT_EQ(followup_event.type, client_->sent_gesture_event().event.type); + EXPECT_EQ(2U, QueuedEventCount()); + + // Our packet is now { Gesture, Key }. + InputMsg_HandleEventPacket::Param param; + ASSERT_EQ(1U, process_->sink().message_count()); + ASSERT_TRUE(InputMsg_HandleEventPacket::Read(process_->sink().GetMessageAt(0), + ¶m)); + EventPacket& followup_packet = param.a; + ASSERT_EQ(2U, followup_packet.size()); + ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT, + followup_packet.events()[0]->payload()->GetType()); + ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT, + followup_packet.events()[1]->payload()->GetType()); + const WebInputEventPayload* payload0 = + WebInputEventPayload::Cast(followup_packet.events()[0]->payload()); + const WebInputEventPayload* payload1 = + WebInputEventPayload::Cast(followup_packet.events()[1]->payload()); + EXPECT_EQ(followup_event.type, payload0->web_event()->type); + EXPECT_EQ(WebInputEvent::RawKeyDown, payload1->web_event()->type); + + // Complete the flush; the gesture should have been ack'ed. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + SCOPED_EXPECT(client_->ExpectDidFlushCalled(true), "DidFlushCalled"); + EXPECT_EQ(followup_event.type, ack_handler_->acked_gesture_event().type); + EXPECT_EQ(0U, QueuedEventCount()); +} + +TEST_F(BufferedInputRouterTest, FlushRequestedOnQueue) { + // The first queued event should trigger a flush request. + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(1U, QueuedEventCount()); + SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(true), "SetNeedsFlushCalled"); + + // Subsequently queued events will not trigger another flush request. + SimulateWheelEvent(5, 0, 0, false); + EXPECT_EQ(2U, QueuedEventCount()); + SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(false), "SetNeedsFlushCalled"); +} + +TEST_F(BufferedInputRouterTest, GetLastKeyboardEvent) { + EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent()); + + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + EXPECT_EQ(WebInputEvent::RawKeyDown, + input_router_->GetLastKeyboardEvent()->type); + + // Queueing another key event does not effect the "last" event. + SimulateKeyboardEvent(WebInputEvent::KeyUp); + EXPECT_EQ(WebInputEvent::RawKeyDown, + input_router_->GetLastKeyboardEvent()->type); + + input_router_->Flush(); + + // Ack'ing the first event should make the second event the "last" event. + InputEventDispositions dispositions; + dispositions.push_back(INPUT_EVENT_IMPL_THREAD_CONSUMED); + dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); + ASSERT_TRUE(FinishFlush(dispositions)); + EXPECT_EQ(WebInputEvent::KeyUp, input_router_->GetLastKeyboardEvent()->type); + + // A key event queued during a flush becomes "last" upon flush completion. + SimulateKeyboardEvent(WebInputEvent::Char); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(WebInputEvent::Char, input_router_->GetLastKeyboardEvent()->type); + + // An empty queue should produce a null "last" event. + input_router_->Flush(); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent()); +} + +TEST_F(BufferedInputRouterTest, UnexpectedAck) { + ASSERT_FALSE(ack_handler_->unexpected_event_ack_called()); + input_router_->OnMessageReceived( + InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions())); + EXPECT_TRUE(ack_handler_->unexpected_event_ack_called()); +} + +TEST_F(BufferedInputRouterTest, BadAck) { + SimulateKeyboardEvent(WebInputEvent::RawKeyDown); + input_router_->Flush(); + + ASSERT_FALSE(ack_handler_->unexpected_event_ack_called()); + EventPacket packet; + input_router_->OnMessageReceived( + InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions())); + EXPECT_TRUE(ack_handler_->unexpected_event_ack_called()); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/immediate_input_router.cc b/chromium/content/browser/renderer_host/input/immediate_input_router.cc index 96e8bb9ac44..a4c69054fd1 100644 --- a/chromium/content/browser/renderer_host/input/immediate_input_router.cc +++ b/chromium/content/browser/renderer_host/input/immediate_input_router.cc @@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/metrics/histogram.h" #include "content/browser/renderer_host/input/gesture_event_filter.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" #include "content/browser/renderer_host/input/input_router_client.h" #include "content/browser/renderer_host/input/touch_event_queue.h" #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" @@ -20,8 +21,8 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/content_switches.h" -#include "ui/base/events/event.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/event.h" +#include "ui/events/keycodes/keyboard_codes.h" using base::Time; using base::TimeDelta; @@ -71,12 +72,13 @@ const char* GetEventAckName(InputEventAckState ack_result) { } // namespace -ImmediateInputRouter::ImmediateInputRouter( - RenderProcessHost* process, - InputRouterClient* client, - int routing_id) +ImmediateInputRouter::ImmediateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* ack_handler, + int routing_id) : process_(process), client_(client), + ack_handler_(ack_handler), routing_id_(routing_id), select_range_pending_(false), move_caret_pending_(false), @@ -85,6 +87,9 @@ ImmediateInputRouter::ImmediateInputRouter( has_touch_handler_(false), touch_event_queue_(new TouchEventQueue(this)), gesture_event_filter_(new GestureEventFilter(this)) { + enable_no_touch_to_renderer_while_scrolling_ = + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoTouchToRendererWhileScrolling); DCHECK(process); DCHECK(client); } @@ -92,20 +97,23 @@ ImmediateInputRouter::ImmediateInputRouter( ImmediateInputRouter::~ImmediateInputRouter() { } -bool ImmediateInputRouter::SendInput(IPC::Message* message) { +void ImmediateInputRouter::Flush() { + NOTREACHED() << "ImmediateInputRouter will never request a flush."; +} + +bool ImmediateInputRouter::SendInput(scoped_ptr<IPC::Message> message) { DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); - scoped_ptr<IPC::Message> scoped_message(message); - switch (scoped_message->type()) { + switch (message->type()) { // Check for types that require an ACK. case InputMsg_SelectRange::ID: - return SendSelectRange(scoped_message.release()); + return SendSelectRange(message.Pass()); case InputMsg_MoveCaret::ID: - return SendMoveCaret(scoped_message.release()); + return SendMoveCaret(message.Pass()); case InputMsg_HandleInputEvent::ID: NOTREACHED() << "WebInputEvents should never be sent via SendInput."; return false; default: - return Send(scoped_message.release()); + return Send(message.release()); } } @@ -199,6 +207,7 @@ void ImmediateInputRouter::SendKeyboardEvent( void ImmediateInputRouter::SendGestureEvent( const GestureEventWithLatencyInfo& gesture_event) { + HandleGestureScroll(gesture_event); if (!client_->OnSendGestureEvent(gesture_event)) return; FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); @@ -252,6 +261,7 @@ void ImmediateInputRouter::SendTouchEventImmediately( void ImmediateInputRouter::SendGestureEventImmediately( const GestureEventWithLatencyInfo& gesture_event) { + HandleGestureScroll(gesture_event); if (!client_->OnSendGestureEventImmediately(gesture_event)) return; FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); @@ -277,10 +287,6 @@ bool ImmediateInputRouter::ShouldForwardGestureEvent( return gesture_event_filter_->ShouldForward(touch_event); } -bool ImmediateInputRouter::HasQueuedGestureEvents() const { - return gesture_event_filter_->HasQueuedGestureEvents(); -} - bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) { bool handled = true; bool message_is_ok = true; @@ -294,7 +300,7 @@ bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) { IPC_END_MESSAGE_MAP() if (!message_is_ok) - client_->OnUnexpectedEventAck(true); + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); return handled; } @@ -302,29 +308,29 @@ bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) { void ImmediateInputRouter::OnTouchEventAck( const TouchEventWithLatencyInfo& event, InputEventAckState ack_result) { - client_->OnTouchEventAck(event, ack_result); + ack_handler_->OnTouchEventAck(event, ack_result); } -bool ImmediateInputRouter::SendSelectRange(IPC::Message* message) { +bool ImmediateInputRouter::SendSelectRange(scoped_ptr<IPC::Message> message) { DCHECK(message->type() == InputMsg_SelectRange::ID); if (select_range_pending_) { - next_selection_range_.reset(message); + next_selection_range_ = message.Pass(); return true; } select_range_pending_ = true; - return Send(message); + return Send(message.release()); } -bool ImmediateInputRouter::SendMoveCaret(IPC::Message* message) { +bool ImmediateInputRouter::SendMoveCaret(scoped_ptr<IPC::Message> message) { DCHECK(message->type() == InputMsg_MoveCaret::ID); if (move_caret_pending_) { - next_move_caret_.reset(message); + next_move_caret_ = message.Pass(); return true; } move_caret_pending_ = true; - return Send(message); + return Send(message.release()); } bool ImmediateInputRouter::Send(IPC::Message* message) { @@ -398,7 +404,7 @@ void ImmediateInputRouter::FilterAndSendWebInputEvent( // Proceed as normal. case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: break; - }; + } // Transmit any pending wheel events on a non-wheel event. This ensures that // the renderer receives the final PhaseEnded wheel event, which is necessary @@ -434,13 +440,13 @@ void ImmediateInputRouter::OnInputEventAck( void ImmediateInputRouter::OnMsgMoveCaretAck() { move_caret_pending_ = false; if (next_move_caret_) - SendMoveCaret(next_move_caret_.release()); + SendMoveCaret(next_move_caret_.Pass()); } void ImmediateInputRouter::OnSelectRangeAck() { select_range_pending_ = false; if (next_selection_range_) - SendSelectRange(next_selection_range_.release()); + SendSelectRange(next_selection_range_.Pass()); } void ImmediateInputRouter::OnHasTouchEventHandlers(bool has_handlers) { @@ -461,7 +467,7 @@ void ImmediateInputRouter::ProcessInputEventAck( int type = static_cast<int>(event_type); if (type < WebInputEvent::Undefined) { - client_->OnUnexpectedEventAck(true); + ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); } else if (type == WebInputEvent::MouseMove) { mouse_move_pending_ = false; @@ -501,23 +507,17 @@ void ImmediateInputRouter::ProcessKeyboardAck( int type, InputEventAckState ack_result) { if (key_queue_.empty()) { - LOG(ERROR) << "Got a KeyEvent back from the renderer but we " - << "don't seem to have sent it to the renderer!"; + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); } else if (key_queue_.front().type != type) { - LOG(ERROR) << "We seem to have a different key type sent from " - << "the renderer. (" << key_queue_.front().type << " vs. " - << type << "). Ignoring event."; - // Something must be wrong. Clear the |key_queue_| and char event // suppression so that we can resume from the error. key_queue_.clear(); - client_->OnUnexpectedEventAck(false); + ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE); } else { NativeWebKeyboardEvent front_item = key_queue_.front(); key_queue_.pop_front(); - client_->OnKeyboardEventAck(front_item, ack_result); - + ack_handler_->OnKeyboardEventAck(front_item, ack_result); // WARNING: This ImmediateInputRouter can be deallocated at this point // (i.e. in the case of Ctrl+W, where the call to // HandleKeyboardEvent destroys this ImmediateInputRouter). @@ -530,7 +530,7 @@ void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) { // Process the unhandled wheel event here before calling // ForwardWheelEventWithLatencyInfo() since it will mutate // current_wheel_event_. - client_->OnWheelEventAck(current_wheel_event_.event, ack_result); + ack_handler_->OnWheelEventAck(current_wheel_event_.event, ack_result); // Now send the next (coalesced) mouse wheel event. if (!coalesced_mouse_wheel_events_.empty()) { @@ -544,7 +544,7 @@ void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) { void ImmediateInputRouter::ProcessGestureAck(int type, InputEventAckState ack_result) { const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result); - client_->OnGestureEventAck( + ack_handler_->OnGestureEventAck( gesture_event_filter_->GetGestureEventAwaitingAck(), ack_result); gesture_event_filter_->ProcessGestureAck(processed, type); } @@ -556,4 +556,19 @@ void ImmediateInputRouter::ProcessTouchAck( touch_event_queue_->ProcessTouchAck(ack_result, latency_info); } +void ImmediateInputRouter::HandleGestureScroll( + const GestureEventWithLatencyInfo& gesture_event) { + if (!enable_no_touch_to_renderer_while_scrolling_) + return; + + // Once scrolling is started stop forwarding touch move events to renderer. + if (gesture_event.event.type == WebInputEvent::GestureScrollBegin) + touch_event_queue_->set_no_touch_move_to_renderer(true); + + if (gesture_event.event.type == WebInputEvent::GestureScrollEnd || + gesture_event.event.type == WebInputEvent::GestureFlingStart) { + touch_event_queue_->set_no_touch_move_to_renderer(false); + } +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/input/immediate_input_router.h b/chromium/content/browser/renderer_host/input/immediate_input_router.h index 270ca3dc67e..1d1a0c0e168 100644 --- a/chromium/content/browser/renderer_host/input/immediate_input_router.h +++ b/chromium/content/browser/renderer_host/input/immediate_input_router.h @@ -21,6 +21,7 @@ struct LatencyInfo; namespace content { class GestureEventFilter; +class InputAckHandler; class InputRouterClient; class RenderProcessHost; class RenderWidgetHostImpl; @@ -33,11 +34,13 @@ class CONTENT_EXPORT ImmediateInputRouter public: ImmediateInputRouter(RenderProcessHost* process, InputRouterClient* client, + InputAckHandler* ack_handler, int routing_id); virtual ~ImmediateInputRouter(); // InputRouter - virtual bool SendInput(IPC::Message* message) OVERRIDE; + virtual void Flush() OVERRIDE; + virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE; virtual void SendMouseEvent( const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; virtual void SendWheelEvent( @@ -59,7 +62,6 @@ class CONTENT_EXPORT ImmediateInputRouter virtual bool ShouldForwardTouchEvent() const OVERRIDE; virtual bool ShouldForwardGestureEvent( const GestureEventWithLatencyInfo& gesture_event) const OVERRIDE; - virtual bool HasQueuedGestureEvents() const OVERRIDE; // IPC::Listener virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; @@ -73,12 +75,14 @@ class CONTENT_EXPORT ImmediateInputRouter } private: + friend class ImmediateInputRouterTest; + // TouchEventQueueClient virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, InputEventAckState ack_result) OVERRIDE; - bool SendMoveCaret(IPC::Message* message); - bool SendSelectRange(IPC::Message* message); + bool SendMoveCaret(scoped_ptr<IPC::Message> message); + bool SendSelectRange(scoped_ptr<IPC::Message> message); bool Send(IPC::Message* message); // Transmits the given input event an as an IPC::Message. This is an internal @@ -127,11 +131,15 @@ private: void ProcessTouchAck(InputEventAckState ack_result, const ui::LatencyInfo& latency_info); + void HandleGestureScroll( + const GestureEventWithLatencyInfo& gesture_event); + int routing_id() const { return routing_id_; } RenderProcessHost* process_; InputRouterClient* client_; + InputAckHandler* ack_handler_; int routing_id_; // (Similar to |mouse_move_pending_|.) True while waiting for SelectRange_ACK. @@ -186,6 +194,10 @@ private: // not sent to the renderer. bool has_touch_handler_; + // Whether enabling the optimization that sending no touch move events to + // renderer while scrolling. + bool enable_no_touch_to_renderer_while_scrolling_; + scoped_ptr<TouchEventQueue> touch_event_queue_; scoped_ptr<GestureEventFilter> gesture_event_filter_; diff --git a/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc b/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc index ed725ee1a10..0e4767a3239 100644 --- a/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc +++ b/chromium/content/browser/renderer_host/input/immediate_input_router_unittest.cc @@ -8,6 +8,8 @@ #include "content/browser/renderer_host/input/gesture_event_filter.h" #include "content/browser/renderer_host/input/immediate_input_router.h" #include "content/browser/renderer_host/input/input_router_client.h" +#include "content/browser/renderer_host/input/input_router_unittest.h" +#include "content/browser/renderer_host/input/mock_input_router_client.h" #include "content/common/content_constants_internal.h" #include "content/common/edit_command.h" #include "content/common/input_messages.h" @@ -15,11 +17,11 @@ #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" #if defined(OS_WIN) || defined(USE_AURA) #include "content/browser/renderer_host/ui_events_helper.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" #endif using base::TimeDelta; @@ -100,266 +102,22 @@ bool EventListIsSubset(const ScopedVector<ui::TouchEvent>& subset, } // namespace -class MockInputRouterClient : public InputRouterClient { +class ImmediateInputRouterTest : public InputRouterTest { public: - MockInputRouterClient() - : input_router_(NULL), - in_flight_event_count_(0), - has_touch_handler_(false), - ack_count_(0), - unexpected_event_ack_called_(false), - ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN), - filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED), - is_shortcut_(false), - allow_send_key_event_(true), - send_called_(false), - send_immediately_called_(false) { - } - virtual ~MockInputRouterClient() { - } - - // InputRouterClient - virtual InputEventAckState FilterInputEvent( - const WebInputEvent& input_event, - const ui::LatencyInfo& latency_info) OVERRIDE { - return filter_state_; - } - - // Called each time a WebInputEvent IPC is sent. - virtual void IncrementInFlightEventCount() OVERRIDE { - ++in_flight_event_count_; - } - - // Called each time a WebInputEvent ACK IPC is received. - virtual void DecrementInFlightEventCount() OVERRIDE { - --in_flight_event_count_; - } - - // Called when the renderer notifies that it has touch event handlers. - virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE { - has_touch_handler_ = has_handlers; - } - - virtual bool OnSendKeyboardEvent( - const NativeWebKeyboardEvent& key_event, - const ui::LatencyInfo& latency_info, - bool* is_shortcut) OVERRIDE { - send_called_ = true; - sent_key_event_ = key_event; - *is_shortcut = is_shortcut_; - - return allow_send_key_event_; - } - - virtual bool OnSendWheelEvent( - const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE { - send_called_ = true; - sent_wheel_event_ = wheel_event; - - return true; - } - - virtual bool OnSendMouseEvent( - const MouseEventWithLatencyInfo& mouse_event) OVERRIDE { - send_called_ = true; - sent_mouse_event_ = mouse_event; - - return true; - } - - virtual bool OnSendTouchEvent( - const TouchEventWithLatencyInfo& touch_event) OVERRIDE { - send_called_ = true; - sent_touch_event_ = touch_event; - - return true; - } - - virtual bool OnSendGestureEvent( - const GestureEventWithLatencyInfo& gesture_event) OVERRIDE { - send_called_ = true; - sent_gesture_event_ = gesture_event; - - return input_router_->ShouldForwardGestureEvent(gesture_event); - } - - virtual bool OnSendMouseEventImmediately( - const MouseEventWithLatencyInfo& mouse_event) OVERRIDE { - send_immediately_called_ = true; - immediately_sent_mouse_event_ = mouse_event; - - return true; - } - - virtual bool OnSendTouchEventImmediately( - const TouchEventWithLatencyInfo& touch_event) OVERRIDE { - send_immediately_called_ = true; - immediately_sent_touch_event_ = touch_event; - - return true; - } - - virtual bool OnSendGestureEventImmediately( - const GestureEventWithLatencyInfo& gesture_event) OVERRIDE { - send_immediately_called_ = true; - immediately_sent_gesture_event_ = gesture_event; - - return true; - } - - // Called upon event ack receipt from the renderer. - virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - acked_key_event_ = event; - RecordAckCalled(ack_result); - } - virtual void OnWheelEventAck(const WebMouseWheelEvent& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - acked_wheel_event_ = event; - RecordAckCalled(ack_result); - } - virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - acked_touch_event_ = event; - RecordAckCalled(ack_result); - } - virtual void OnGestureEventAck(const WebGestureEvent& event, - InputEventAckState ack_result) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - RecordAckCalled(ack_result); - } - virtual void OnUnexpectedEventAck(bool bad_message) OVERRIDE { - VLOG(1) << __FUNCTION__ << " called!"; - unexpected_event_ack_called_ = true; - } - - void ExpectSendCalled(bool called) { - EXPECT_EQ(called, send_called_); - send_called_ = false; - } - void ExpectSendImmediatelyCalled(bool called) { - EXPECT_EQ(called, send_immediately_called_); - send_immediately_called_ = false; - } - void ExpectAckCalled(int times) { - EXPECT_EQ(times, ack_count_); - ack_count_ = 0; - } - - void set_input_router(InputRouter* input_router) { - input_router_ = input_router; - } - bool has_touch_handler() const { return has_touch_handler_; } - InputEventAckState ack_state() const { return ack_state_; } - void set_filter_state(InputEventAckState filter_state) { - filter_state_ = filter_state; - } - bool unexpected_event_ack_called() const { - return unexpected_event_ack_called_; - } - const NativeWebKeyboardEvent& acked_keyboard_event() { - return acked_key_event_; - } - const WebMouseWheelEvent& acked_wheel_event() { - return acked_wheel_event_; - } - const TouchEventWithLatencyInfo& acked_touch_event() { - return acked_touch_event_; - } - void set_is_shortcut(bool is_shortcut) { - is_shortcut_ = is_shortcut; - } - void set_allow_send_key_event(bool allow) { - allow_send_key_event_ = allow; - } - const NativeWebKeyboardEvent& sent_key_event() { - return sent_key_event_; - } - const MouseWheelEventWithLatencyInfo& sent_wheel_event() { - return sent_wheel_event_; - } - const MouseEventWithLatencyInfo& sent_mouse_event() { - return sent_mouse_event_; - } - const GestureEventWithLatencyInfo& sent_gesture_event() { - return sent_gesture_event_; - } - const MouseEventWithLatencyInfo& immediately_sent_mouse_event() { - return immediately_sent_mouse_event_; - } - const TouchEventWithLatencyInfo& immediately_sent_touch_event() { - return immediately_sent_touch_event_; - } - const GestureEventWithLatencyInfo& immediately_sent_gesture_event() { - return immediately_sent_gesture_event_; - } - - private: - void RecordAckCalled(InputEventAckState ack_result) { - ++ack_count_; - ack_state_ = ack_result; - } - - InputRouter* input_router_; - int in_flight_event_count_; - bool has_touch_handler_; - - int ack_count_; - bool unexpected_event_ack_called_; - InputEventAckState ack_state_; - InputEventAckState filter_state_; - NativeWebKeyboardEvent acked_key_event_; - WebMouseWheelEvent acked_wheel_event_; - TouchEventWithLatencyInfo acked_touch_event_; - - bool is_shortcut_; - bool allow_send_key_event_; - bool send_called_; - NativeWebKeyboardEvent sent_key_event_; - MouseWheelEventWithLatencyInfo sent_wheel_event_; - MouseEventWithLatencyInfo sent_mouse_event_; - TouchEventWithLatencyInfo sent_touch_event_; - GestureEventWithLatencyInfo sent_gesture_event_; - - bool send_immediately_called_; - MouseEventWithLatencyInfo immediately_sent_mouse_event_; - TouchEventWithLatencyInfo immediately_sent_touch_event_; - GestureEventWithLatencyInfo immediately_sent_gesture_event_; -}; - -class ImmediateInputRouterTest : public testing::Test { - public: - ImmediateInputRouterTest() { - } - virtual ~ImmediateInputRouterTest() { - } + ImmediateInputRouterTest() {} + virtual ~ImmediateInputRouterTest() {} protected: - // testing::Test - virtual void SetUp() { - browser_context_.reset(new TestBrowserContext()); - process_.reset(new MockRenderProcessHost(browser_context_.get())); - client_.reset(new MockInputRouterClient()); - input_router_.reset(new ImmediateInputRouter( - process_.get(), - client_.get(), - MSG_ROUTING_NONE)); - client_->set_input_router(input_router_.get()); - } - virtual void TearDown() { - // Process all pending tasks to avoid leaks. - base::MessageLoop::current()->RunUntilIdle(); - - input_router_.reset(); - client_.reset(); - process_.reset(); - browser_context_.reset(); + // InputRouterTest + virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* handler, + int routing_id) OVERRIDE { + return scoped_ptr<InputRouter>( + new ImmediateInputRouter(process, client, handler, routing_id)); } - void SendInputEventACK(WebInputEvent::Type type, + void SendInputEventACK(WebKit::WebInputEvent::Type type, InputEventAckState ack_result) { scoped_ptr<IPC::Message> response( new InputHostMsg_HandleInputEvent_ACK(0, type, ack_result, @@ -367,177 +125,16 @@ class ImmediateInputRouterTest : public testing::Test { input_router_->OnMessageReceived(*response); } - void SimulateKeyboardEvent(WebInputEvent::Type type) { - NativeWebKeyboardEvent key_event; - key_event.type = type; - key_event.windowsKeyCode = ui::VKEY_L; // non-null made up value. - input_router_->SendKeyboardEvent(key_event, ui::LatencyInfo()); - client_->ExpectSendCalled(true); - EXPECT_EQ(type, client_->sent_key_event().type); - EXPECT_EQ(key_event.windowsKeyCode, - client_->sent_key_event().windowsKeyCode); - } - - void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) { - WebMouseWheelEvent wheel_event; - wheel_event.type = WebInputEvent::MouseWheel; - wheel_event.deltaX = dX; - wheel_event.deltaY = dY; - wheel_event.modifiers = modifiers; - wheel_event.hasPreciseScrollingDeltas = precise; - input_router_->SendWheelEvent( - MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); - client_->ExpectSendCalled(true); - EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); - EXPECT_EQ(dX, client_->sent_wheel_event().event.deltaX); - } - - void SimulateMouseMove(int x, int y, int modifiers) { - WebMouseEvent mouse_event; - mouse_event.type = WebInputEvent::MouseMove; - mouse_event.x = mouse_event.windowX = x; - mouse_event.y = mouse_event.windowY = y; - mouse_event.modifiers = modifiers; - input_router_->SendMouseEvent( - MouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo())); - client_->ExpectSendCalled(true); - client_->ExpectSendImmediatelyCalled(true); - EXPECT_EQ(mouse_event.type, client_->sent_mouse_event().event.type); - EXPECT_EQ(x, client_->sent_mouse_event().event.x); - EXPECT_EQ(mouse_event.type, - client_->immediately_sent_mouse_event().event.type); - EXPECT_EQ(x, client_->immediately_sent_mouse_event().event.x); - } - - void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) { - WebMouseWheelEvent wheel_event; - wheel_event.type = WebInputEvent::MouseWheel; - wheel_event.phase = phase; - input_router_->SendWheelEvent( - MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); - client_->ExpectSendCalled(true); - EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); - EXPECT_EQ(phase, client_->sent_wheel_event().event.phase); - } - - // Inject provided synthetic WebGestureEvent instance. - void SimulateGestureEventCore(WebInputEvent::Type type, - WebGestureEvent::SourceDevice sourceDevice, - WebGestureEvent* gesture_event) { - gesture_event->type = type; - gesture_event->sourceDevice = sourceDevice; - GestureEventWithLatencyInfo gesture_with_latency( - *gesture_event, ui::LatencyInfo()); - input_router_->SendGestureEvent(gesture_with_latency); - client_->ExpectSendCalled(true); - EXPECT_EQ(type, client_->sent_gesture_event().event.type); - EXPECT_EQ(sourceDevice, client_->sent_gesture_event().event.sourceDevice); - } - - // Inject simple synthetic WebGestureEvent instances. - void SimulateGestureEvent(WebInputEvent::Type type, - WebGestureEvent::SourceDevice sourceDevice) { - WebGestureEvent gesture_event; - SimulateGestureEventCore(type, sourceDevice, &gesture_event); - } - - void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) { - WebGestureEvent gesture_event; - gesture_event.data.scrollUpdate.deltaX = dX; - gesture_event.data.scrollUpdate.deltaY = dY; - gesture_event.modifiers = modifiers; - SimulateGestureEventCore(WebInputEvent::GestureScrollUpdate, - WebGestureEvent::Touchscreen, &gesture_event); - } - - void SimulateGesturePinchUpdateEvent(float scale, - float anchorX, - float anchorY, - int modifiers) { - WebGestureEvent gesture_event; - gesture_event.data.pinchUpdate.scale = scale; - gesture_event.x = anchorX; - gesture_event.y = anchorY; - gesture_event.modifiers = modifiers; - SimulateGestureEventCore(WebInputEvent::GesturePinchUpdate, - WebGestureEvent::Touchscreen, &gesture_event); - } - - // Inject synthetic GestureFlingStart events. - void SimulateGestureFlingStartEvent( - float velocityX, - float velocityY, - WebGestureEvent::SourceDevice sourceDevice) { - WebGestureEvent gesture_event; - gesture_event.data.flingStart.velocityX = velocityX; - gesture_event.data.flingStart.velocityY = velocityY; - SimulateGestureEventCore(WebInputEvent::GestureFlingStart, sourceDevice, - &gesture_event); - } - - // Set the timestamp for the touch-event. - void SetTouchTimestamp(base::TimeDelta timestamp) { - touch_event_.timeStampSeconds = timestamp.InSecondsF(); - } - - // Sends a touch event (irrespective of whether the page has a touch-event - // handler or not). - void SendTouchEvent() { - input_router_->SendTouchEvent( - TouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo())); - - // Mark all the points as stationary. And remove the points that have been - // released. - int point = 0; - for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) { - if (touch_event_.touches[i].state == WebTouchPoint::StateReleased) - continue; - - touch_event_.touches[point] = touch_event_.touches[i]; - touch_event_.touches[point].state = - WebTouchPoint::StateStationary; - ++point; - } - touch_event_.touchesLength = point; - touch_event_.type = WebInputEvent::Undefined; - } - - int PressTouchPoint(int x, int y) { - if (touch_event_.touchesLength == touch_event_.touchesLengthCap) - return -1; - WebTouchPoint& point = - touch_event_.touches[touch_event_.touchesLength]; - point.id = touch_event_.touchesLength; - point.position.x = point.screenPosition.x = x; - point.position.y = point.screenPosition.y = y; - point.state = WebTouchPoint::StatePressed; - point.radiusX = point.radiusY = 1.f; - ++touch_event_.touchesLength; - touch_event_.type = WebInputEvent::TouchStart; - return point.id; - } - - void MoveTouchPoint(int index, int x, int y) { - CHECK(index >= 0 && index < touch_event_.touchesLengthCap); - WebTouchPoint& point = touch_event_.touches[index]; - point.position.x = point.screenPosition.x = x; - point.position.y = point.screenPosition.y = y; - touch_event_.touches[index].state = WebTouchPoint::StateMoved; - touch_event_.type = WebInputEvent::TouchMove; - } - - void ReleaseTouchPoint(int index) { - CHECK(index >= 0 && index < touch_event_.touchesLengthCap); - touch_event_.touches[index].state = WebTouchPoint::StateReleased; - touch_event_.type = WebInputEvent::TouchEnd; + ImmediateInputRouter* input_router() const { + return static_cast<ImmediateInputRouter*>(input_router_.get()); } void set_debounce_interval_time_ms(int ms) { - input_router_->gesture_event_filter()->debounce_interval_time_ms_ = ms; + input_router()->gesture_event_filter()->debounce_interval_time_ms_ = ms; } void set_maximum_tap_gap_time_ms(int delay_ms) { - input_router_->gesture_event_filter()->maximum_tap_gap_time_ms_ = delay_ms; + input_router()->gesture_event_filter()->maximum_tap_gap_time_ms_ = delay_ms; } size_t TouchEventQueueSize() { @@ -548,8 +145,16 @@ class ImmediateInputRouterTest : public testing::Test { return touch_event_queue()->GetLatestEvent().event; } + void EnableNoTouchToRendererWhileScrolling() { + input_router()->enable_no_touch_to_renderer_while_scrolling_ = true; + } + + bool no_touch_move_to_renderer() { + return touch_event_queue()->no_touch_move_to_renderer_; + } + TouchEventQueue* touch_event_queue() const { - return input_router_->touch_event_queue(); + return input_router()->touch_event_queue(); } unsigned GestureEventLastQueueEventSize() { @@ -590,18 +195,8 @@ class ImmediateInputRouterTest : public testing::Test { } GestureEventFilter* gesture_event_filter() const { - return input_router_->gesture_event_filter(); + return input_router()->gesture_event_filter(); } - - scoped_ptr<MockRenderProcessHost> process_; - scoped_ptr<MockInputRouterClient> client_; - scoped_ptr<ImmediateInputRouter> input_router_; - - private: - base::MessageLoopForUI message_loop_; - WebTouchEvent touch_event_; - - scoped_ptr<TestBrowserContext> browser_context_; }; #if GTEST_HAS_PARAM_TEST @@ -613,8 +208,8 @@ class ImmediateInputRouterWithSourceTest #endif // GTEST_HAS_PARAM_TEST TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) { - input_router_->SendInput( - new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4))); + input_router_->SendInput(scoped_ptr<IPC::Message>( + new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4)))); EXPECT_EQ(1u, process_->sink().message_count()); ExpectIPCMessageWithArg2<InputMsg_SelectRange>( process_->sink().GetMessageAt(0), @@ -623,12 +218,12 @@ TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) { process_->sink().ClearMessages(); // Send two more messages without acking. - input_router_->SendInput( - new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8))); + input_router_->SendInput(scoped_ptr<IPC::Message>( + new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8)))); EXPECT_EQ(0u, process_->sink().message_count()); - input_router_->SendInput( - new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12))); + input_router_->SendInput(scoped_ptr<IPC::Message>( + new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12)))); EXPECT_EQ(0u, process_->sink().message_count()); // Now ack the first message. @@ -654,17 +249,20 @@ TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) { } TEST_F(ImmediateInputRouterTest, CoalescesCaretMove) { - input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(1, 2))); + input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(1, 2)))); EXPECT_EQ(1u, process_->sink().message_count()); ExpectIPCMessageWithArg1<InputMsg_MoveCaret>( process_->sink().GetMessageAt(0), gfx::Point(1, 2)); process_->sink().ClearMessages(); // Send two more messages without acking. - input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(5, 6))); + input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(5, 6)))); EXPECT_EQ(0u, process_->sink().message_count()); - input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(9, 10))); + input_router_->SendInput( + scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(9, 10)))); EXPECT_EQ(0u, process_->sink().message_count()); // Now ack the first message. @@ -697,7 +295,7 @@ TEST_F(ImmediateInputRouterTest, HandledInputEvent) { EXPECT_EQ(0u, process_->sink().message_count()); // OnKeyboardEventAck should be triggered without actual ack. - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); // As the event was acked already, keyboard event queue should be // empty. @@ -705,14 +303,14 @@ TEST_F(ImmediateInputRouterTest, HandledInputEvent) { } TEST_F(ImmediateInputRouterTest, ClientCanceledKeyboardEvent) { - client_->set_allow_send_key_event(false); + client_->set_allow_send_event(false); // Simulate a keyboard event. SimulateKeyboardEvent(WebInputEvent::RawKeyDown); // Make sure no input event is sent to the renderer. EXPECT_EQ(0u, process_->sink().message_count()); - client_->ExpectAckCalled(0); + ack_handler_->ExpectAckCalled(0); } TEST_F(ImmediateInputRouterTest, ShortcutKeyboardEvent) { @@ -734,7 +332,7 @@ TEST_F(ImmediateInputRouterTest, NoncorrespondingKeyEvents) { SendInputEventACK(WebInputEvent::KeyUp, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_TRUE(client_->unexpected_event_ack_called()); + EXPECT_TRUE(ack_handler_->unexpected_event_ack_called()); } // Tests ported from RenderWidgetHostTest -------------------------------------- @@ -754,8 +352,9 @@ TEST_F(ImmediateInputRouterTest, HandleKeyEventsWeSent) { // Send the simulated response from the renderer back. SendInputEventACK(WebInputEvent::RawKeyDown, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - client_->ExpectAckCalled(1); - EXPECT_EQ(WebInputEvent::RawKeyDown, client_->acked_keyboard_event().type); + ack_handler_->ExpectAckCalled(1); + EXPECT_EQ(WebInputEvent::RawKeyDown, + ack_handler_->acked_keyboard_event().type); } TEST_F(ImmediateInputRouterTest, IgnoreKeyEventsWeDidntSend) { @@ -763,7 +362,7 @@ TEST_F(ImmediateInputRouterTest, IgnoreKeyEventsWeDidntSend) { SendInputEventACK(WebInputEvent::RawKeyDown, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - client_->ExpectAckCalled(0); + ack_handler_->ExpectAckCalled(0); } TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { @@ -786,7 +385,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { // so that additional input events can be processed before // we turn off coalescing. base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -796,7 +395,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -806,7 +405,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) { SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, process_->sink().message_count()); // FIXME(kouhei): Below is testing gesture event filter. Maybe separate test? @@ -909,7 +508,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollBegin, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -919,7 +518,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -929,7 +528,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); @@ -939,7 +538,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) { SendInputEventACK(WebInputEvent::GestureScrollEnd, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, process_->sink().message_count()); } @@ -1192,14 +791,14 @@ TEST_P(ImmediateInputRouterWithSourceTest, GestureFlingCancelsFiltered) { SendInputEventACK(WebInputEvent::GestureFlingStart, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device); EXPECT_FALSE(FlingInProgress()); EXPECT_EQ(2U, process_->sink().message_count()); SendInputEventACK(WebInputEvent::GestureFlingCancel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, GestureEventLastQueueEventSize()); // GFC before previous GFS is acked. @@ -1218,7 +817,7 @@ TEST_P(ImmediateInputRouterWithSourceTest, GestureFlingCancelsFiltered) { SendInputEventACK(WebInputEvent::GestureFlingCancel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); - client_->ExpectAckCalled(2); + ack_handler_->ExpectAckCalled(2); EXPECT_EQ(0U, GestureEventLastQueueEventSize()); // GFS is added to the queue if another event is pending @@ -1563,18 +1162,18 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueue) { SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, TouchEventQueueSize()); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(WebInputEvent::TouchStart, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, TouchEventQueueSize()); - client_->ExpectAckCalled(1); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); EXPECT_EQ(0U, process_->sink().message_count()); } @@ -1615,8 +1214,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueFlush) { INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(31U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchStart, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); @@ -1663,8 +1262,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) { EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_EQ(2U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchStart, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); process_->sink().ClearMessages(); // Coalesced touch-move events should be sent. @@ -1678,8 +1277,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) { EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_EQ(1U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(10); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(10); process_->sink().ClearMessages(); // ACK the release. @@ -1688,8 +1287,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) { EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(0U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchEnd, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); } // Tests that an event that has already been sent but hasn't been ack'ed yet @@ -1844,8 +1443,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); EXPECT_EQ(0U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(2); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(2); EXPECT_EQ(0U, process_->sink().message_count()); process_->sink().ClearMessages(); @@ -1854,8 +1453,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { SendTouchEvent(); EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(WebInputEvent::TouchEnd, - client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); // Send a press-event, followed by move and release events, and another press // event, before the ACK for the first press event comes back. All of the @@ -1883,8 +1482,9 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); EXPECT_EQ(1U, process_->sink().message_count()); - EXPECT_EQ(WebInputEvent::TouchEnd, client_->acked_touch_event().event.type); - client_->ExpectAckCalled(4); + EXPECT_EQ(WebInputEvent::TouchEnd, + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(4); EXPECT_EQ(1U, TouchEventQueueSize()); process_->sink().ClearMessages(); @@ -1892,8 +1492,9 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) { SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); EXPECT_EQ(0U, process_->sink().message_count()); - EXPECT_EQ(WebInputEvent::TouchStart, client_->acked_touch_event().event.type); - client_->ExpectAckCalled(1); + EXPECT_EQ(WebInputEvent::TouchStart, + ack_handler_->acked_touch_event().event.type); + ack_handler_->ExpectAckCalled(1); EXPECT_EQ(0U, TouchEventQueueSize()); // Send a second press event. Even though the first touch had NO_CONSUMER, @@ -2030,7 +1631,7 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueConsumerIgnoreMultiFinger) { EXPECT_EQ(1U, process_->sink().message_count()); EXPECT_EQ(2U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); process_->sink().ClearMessages(); // ACK the press with NO_CONSUMED_EXISTS. This should release the queued @@ -2040,7 +1641,7 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueConsumerIgnoreMultiFinger) { EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(0U, TouchEventQueueSize()); EXPECT_EQ(WebInputEvent::TouchMove, - client_->acked_touch_event().event.type); + ack_handler_->acked_touch_event().event.type); ReleaseTouchPoint(2); ReleaseTouchPoint(1); @@ -2118,11 +1719,11 @@ TEST_F(ImmediateInputRouterTest, AckedTouchEventState) { for (size_t i = 0; i < arraysize(acks); ++i) { SendInputEventACK(acks[i], INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(acks[i], client_->acked_touch_event().event.type); + EXPECT_EQ(acks[i], ack_handler_->acked_touch_event().event.type); ScopedVector<ui::TouchEvent> acked; MakeUITouchEventsFromWebTouchEvents( - client_->acked_touch_event(), &acked, coordinate_system); + ack_handler_->acked_touch_event(), &acked, coordinate_system); bool success = EventListIsSubset(acked, expected_events); EXPECT_TRUE(success) << "Failed on step: " << i; if (!success) @@ -2151,9 +1752,9 @@ TEST_F(ImmediateInputRouterTest, UnhandledWheelEvent) { INPUT_EVENT_ACK_STATE_NOT_CONSUMED); // Check that the correct unhandled wheel event was received. - client_->ExpectAckCalled(1); - EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, client_->ack_state()); - EXPECT_EQ(client_->acked_wheel_event().deltaY, -5); + ack_handler_->ExpectAckCalled(1); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state()); + EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5); // Check that the second event was sent. EXPECT_EQ(1U, process_->sink().message_count()); @@ -2162,7 +1763,96 @@ TEST_F(ImmediateInputRouterTest, UnhandledWheelEvent) { process_->sink().ClearMessages(); // Check that the correct unhandled wheel event was received. - EXPECT_EQ(client_->acked_wheel_event().deltaY, -5); + EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5); } +// Tests that no touch move events are sent to renderer during scrolling. +TEST_F(ImmediateInputRouterTest, NoTouchMoveWhileScroll) { + EnableNoTouchToRendererWhileScrolling(); + set_debounce_interval_time_ms(0); + input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); + process_->sink().ClearMessages(); + + // First touch press. + PressTouchPoint(0, 1); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchStart, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Touch move will trigger scroll. + MoveTouchPoint(0, 20, 5); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + WebGestureEvent::Touchscreen); + EXPECT_EQ(1U, process_->sink().message_count()); + EXPECT_TRUE(no_touch_move_to_renderer()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::GestureScrollBegin, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Touch move should not be sent to renderer. + MoveTouchPoint(0, 30, 5); + SendTouchEvent(); + EXPECT_EQ(0U, process_->sink().message_count()); + process_->sink().ClearMessages(); + + // Touch moves become ScrollUpdate. + SimulateGestureScrollUpdateEvent(20, 4, 0); + EXPECT_TRUE(no_touch_move_to_renderer()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Touch move should not be sent to renderer. + MoveTouchPoint(0, 65, 10); + SendTouchEvent(); + EXPECT_EQ(0U, process_->sink().message_count()); + process_->sink().ClearMessages(); + + // Touch end should still be sent to renderer. + ReleaseTouchPoint(0); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchEnd, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // On GestureScrollEnd, resume sending touch moves to renderer. + SimulateGestureEvent(WebKit::WebInputEvent::GestureScrollEnd, + WebGestureEvent::Touchscreen); + EXPECT_EQ(1U, process_->sink().message_count()); + EXPECT_FALSE(no_touch_move_to_renderer()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::GestureScrollEnd, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Now touch events should come through to renderer. + PressTouchPoint(80, 10); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchStart, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + MoveTouchPoint(0, 80, 20); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + ReleaseTouchPoint(0); + SendTouchEvent(); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::TouchEnd, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); +} } // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_ack_handler.h b/chromium/content/browser/renderer_host/input/input_ack_handler.h new file mode 100644 index 00000000000..3e09b07cd03 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_ack_handler.h @@ -0,0 +1,41 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_ + +#include "base/basictypes.h" +#include "content/port/browser/event_with_latency_info.h" +#include "content/port/common/input_event_ack_state.h" +#include "content/public/browser/native_web_keyboard_event.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +namespace content { + +// Provided customized ack response for input events. +class CONTENT_EXPORT InputAckHandler { + public: + virtual ~InputAckHandler() {} + + // Called upon event ack receipt from the renderer. + virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, + InputEventAckState ack_result) = 0; + virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, + InputEventAckState ack_result) = 0; + virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, + InputEventAckState ack_result) = 0; + virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, + InputEventAckState ack_result) = 0; + + enum UnexpectedEventAckType { + UNEXPECTED_ACK, + UNEXPECTED_EVENT_TYPE, + BAD_ACK_MESSAGE + }; + virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_ diff --git a/chromium/content/browser/renderer_host/input/input_queue.cc b/chromium/content/browser/renderer_host/input/input_queue.cc new file mode 100644 index 00000000000..c10500b9c08 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue.cc @@ -0,0 +1,183 @@ +// Copyright 2013 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/browser/renderer_host/input/input_queue.h" + +#include "base/bind.h" +#include "base/debug/trace_event.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_queue_client.h" +#include "content/common/input/event_packet.h" +#include "content/common/input/input_event.h" + +namespace content { + +// A specialized EventPacket with utility methods for dispatched event handling. +class InputQueue::BrowserEventPacket : public EventPacket { + public: + typedef ScopedVector<BrowserInputEvent> BrowserInputEvents; + + BrowserEventPacket() : weak_factory_(this) {} + virtual ~BrowserEventPacket() {} + + // Validate the response and signal dispatch to the processed events. + // Undelivered events will be re-enqueued, and any generated followup events + // will be inserted at the same relative order as their generating event. + AckResult ValidateAndDispatchAck(int64 packet_id, + const InputEventDispositions& dispositions) { + if (!Validate(packet_id, dispositions)) + return ACK_INVALID; + + // Empty the packet; events will be re-enqueued as necessary. + InputEvents dispatched_events; + events_.swap(dispatched_events); + + // The packet could be deleted as a result of event dispatch; use a local + // weak ref to ensure proper shutdown. + base::WeakPtr<EventPacket> weak_ref_this = weak_factory_.GetWeakPtr(); + + BrowserInputEvents followup_events; + for (size_t i = 0; i < dispatched_events.size(); ++i) { + // Take ownership of the event. + scoped_ptr<BrowserInputEvent> event( + static_cast<BrowserInputEvent*>(dispatched_events[i])); + dispatched_events[i] = NULL; + + // Re-enqueue undelivered events. + InputEventDisposition disposition = dispositions[i]; + if (disposition == INPUT_EVENT_COULD_NOT_DELIVER) { + Add(event.PassAs<InputEvent>()); + continue; + } + + event->OnDispatched(disposition, &followup_events); + + // TODO(jdduke): http://crbug.com/274029 + if (!weak_ref_this.get()) + return ACK_SHUTDOWN; + + AddAll(&followup_events); + } + return ACK_OK; + } + + protected: + // Add and take ownership of events in |followup_events|. + void AddAll(BrowserInputEvents* followup_events) { + for (BrowserInputEvents::iterator iter = followup_events->begin(); + iter != followup_events->end(); + ++iter) { + Add(scoped_ptr<InputEvent>(*iter)); + } + followup_events->weak_clear(); + } + + // Perform a sanity check of the ack against the current packet. + // |packet_id| should match that of this packet, and |dispositions| should + // be of size equal to the number of events in this packet. + bool Validate(int64 packet_id, + const InputEventDispositions& dispositions) const { + if (packet_id != id()) + return false; + + if (dispositions.size() != size()) + return false; + + return true; + } + + private: + base::WeakPtrFactory<EventPacket> weak_factory_; +}; + +InputQueue::InputQueue(InputQueueClient* client) + : client_(client), + next_packet_id_(1), + flush_requested_(false), + in_flush_packet_(new BrowserEventPacket()), + pending_flush_packet_(new BrowserEventPacket()) { + DCHECK(client_); +} + +InputQueue::~InputQueue() {} + +void InputQueue::QueueEvent(scoped_ptr<BrowserInputEvent> event) { + DCHECK(event); + DCHECK(event->valid()); + pending_flush_packet_->Add(event.PassAs<InputEvent>()); + RequestFlushIfNecessary(); +} + +void InputQueue::BeginFlush() { + // Ignore repeated flush attempts. + if (!flush_requested_) + return; + + DCHECK(!FlushInProgress()); + DCHECK(!pending_flush_packet_->empty()); + + flush_requested_ = false; + in_flush_packet_.swap(pending_flush_packet_); + DeliverInFlushPacket(); +} + +InputQueue::AckResult InputQueue::OnEventPacketAck( + int64 packet_id, + const InputEventDispositions& dispositions) { + if (!FlushInProgress()) + return ACK_UNEXPECTED; + + TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "AckPacket", + "id", packet_id); + + AckResult ack_result = + in_flush_packet_->ValidateAndDispatchAck(packet_id, dispositions); + + if (ack_result != ACK_OK) + return ack_result; + + if (FlushInProgress()) { + DeliverInFlushPacket(); + } else { + TRACE_EVENT_ASYNC_END0("input", "InputQueueFlush", this); + client_->DidFinishFlush(); + RequestFlushIfNecessary(); + } + + return ACK_OK; +} + +size_t InputQueue::QueuedEventCount() const { + return in_flush_packet_->size() + pending_flush_packet_->size(); +} + +void InputQueue::DeliverInFlushPacket() { + DCHECK(FlushInProgress()); + TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "DeliverPacket", + "id", next_packet_id_); + in_flush_packet_->set_id(next_packet_id_++); + client_->Deliver(*in_flush_packet_); +} + +void InputQueue::RequestFlushIfNecessary() { + if (flush_requested_) + return; + + // Defer flush requests until the current flush has finished. + if (FlushInProgress()) + return; + + // No additional events to flush. + if (pending_flush_packet_->empty()) + return; + + TRACE_EVENT_ASYNC_BEGIN0("input", "InputQueueFlush", this); + TRACE_EVENT_ASYNC_STEP0("input", "InputQueueFlush", this, "Request"); + flush_requested_ = true; + client_->SetNeedsFlush(); +} + +bool InputQueue::FlushInProgress() const { return !in_flush_packet_->empty(); } + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_queue.h b/chromium/content/browser/renderer_host/input/input_queue.h new file mode 100644 index 00000000000..2c7acb707d2 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue.h @@ -0,0 +1,82 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/common/input/input_event_disposition.h" + +namespace content { + +class BrowserInputEvent; +class EventPacketAck; +class InputQueueClient; + +// |InputQueue| handles browser input event batching and response. +// Event packets are assembled into sequential event packets. A flush entails +// delivery and dispatch of a single event packet, and continues until the +// packet is ack'ed and all its events have been dispatched to the renderer. +class CONTENT_EXPORT InputQueue { + public: + // The |client| should outlive the InputQueue. + InputQueue(InputQueueClient* client); + virtual ~InputQueue(); + + // If a flush is in progress, |event| will be dispatched in the next flush. + // If no flush is in progress, a flush will be requested if necessary. + // |event| is assumed to be both non-NULL and valid. + void QueueEvent(scoped_ptr<BrowserInputEvent> event); + + // Initiates the flush of the pending event packet to |client_|, if necessary. + // This should only be called in response to |client_->SetNeedsFlush()|. + void BeginFlush(); + + // Called by the owner upon EventPacket responses from the sender target. This + // will dispatch event acks for events with a corresponding |ack_handler|. + enum AckResult { + ACK_OK, // |acked_packet| was processed successfully. + ACK_UNEXPECTED, // |acked_packet| was unexpected; no flush was in-progress. + ACK_INVALID, // |acked_packet| contains invalid data. + ACK_SHUTDOWN // |acked_packet| processing triggered queue shutdown. + }; + AckResult OnEventPacketAck(int64 packet_id, + const InputEventDispositions& dispositions); + + // Total number of evenst in the queue, both being flushed and pending flush. + size_t QueuedEventCount() const; + + protected: + friend class InputQueueTest; + + // Delivers |in_flush_packet_| to the client. + void DeliverInFlushPacket(); + + // Requests a flush via |client_| if the necessary request has not been made. + void RequestFlushIfNecessary(); + + // True when |in_flush_packet_| is non-empty. + bool FlushInProgress() const; + + private: + InputQueueClient* client_; + + // Used to assign unique, non-zero ID's to each delivered EventPacket. + int64 next_packet_id_; + + // Avoid spamming the client with redundant flush requests. + bool flush_requested_; + + class BrowserEventPacket; + scoped_ptr<BrowserEventPacket> in_flush_packet_; + scoped_ptr<BrowserEventPacket> pending_flush_packet_; + + DISALLOW_COPY_AND_ASSIGN(InputQueue); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_ diff --git a/chromium/content/browser/renderer_host/input/input_queue_client.h b/chromium/content/browser/renderer_host/input/input_queue_client.h new file mode 100644 index 00000000000..f1151d92fed --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue_client.h @@ -0,0 +1,35 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_vector.h" +#include "content/common/input/input_event_disposition.h" + +namespace content { + +class EventPacket; + +class InputQueueClient { + public: + virtual ~InputQueueClient() {} + + // Used by the flush process for sending batched events to the desired target. + virtual void Deliver(const EventPacket& packet) = 0; + + // Signalled when the InputQueue has received and dispatched + // event ACK's from the previous flush. + virtual void DidFinishFlush() = 0; + + // Signalled when the InputQueue receives an input event. + virtual void SetNeedsFlush() = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_ diff --git a/chromium/content/browser/renderer_host/input/input_queue_unittest.cc b/chromium/content/browser/renderer_host/input/input_queue_unittest.cc new file mode 100644 index 00000000000..2d183d96cca --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_queue_unittest.cc @@ -0,0 +1,363 @@ +// Copyright 2013 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 <vector> + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/browser_input_event.h" +#include "content/browser/renderer_host/input/input_queue.h" +#include "content/browser/renderer_host/input/input_queue_client.h" +#include "content/common/input/event_packet.h" +#include "content/common/input/input_event.h" +#include "content/common/input/ipc_input_event_payload.h" +#include "content/common/input/web_input_event_payload.h" +#include "content/common/input_messages.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/latency_info.h" + +namespace content { +namespace { + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebKeyboardEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; + +class InputQueueTest : public testing::Test, + public InputQueueClient, + public BrowserInputEventClient { + public: + InputQueueTest() + : queue_(new InputQueue(this)), + routing_id_(0), + num_flush_completions_(0), + num_flush_requests_(0), + num_packet_deliveries_(0), + next_input_id_(1) {} + + // InputQueueClient + virtual void Deliver(const EventPacket& packet) OVERRIDE { + EXPECT_LT(0u, packet.size()); + ++num_packet_deliveries_; + current_packet_id_ = packet.id(); + current_packet_dispositions_.resize(packet.size(), INPUT_EVENT_UNHANDLED); + } + + virtual void DidFinishFlush() OVERRIDE { ++num_flush_completions_; } + virtual void SetNeedsFlush() OVERRIDE { ++num_flush_requests_; } + + // BrowserInputEventClient + virtual void OnDispatched(const BrowserInputEvent& event, + InputEventDisposition disposition) OVERRIDE { + acked_dispositions_.push_back(disposition); + } + + virtual void OnDispatched( + const BrowserInputEvent& event, + InputEventDisposition disposition, + ScopedVector<BrowserInputEvent>* followup) OVERRIDE { + acked_followup_dispositions_.push_back(disposition); + if (event_to_inject_) + followup->push_back(event_to_inject_.release()); + } + + int num_flush_requests() const { return num_flush_requests_; } + int num_flush_completions() const { return num_flush_completions_; } + int num_packet_deliveries() const { return num_packet_deliveries_; } + + protected: + scoped_ptr<BrowserInputEvent> CreateIPCInputEvent(IPC::Message* message) { + return BrowserInputEvent::Create( + NextInputID(), + IPCInputEventPayload::Create(make_scoped_ptr(message)), + this); + } + + scoped_ptr<BrowserInputEvent> CreateWebInputEvent( + WebInputEvent::Type web_type) { + WebKit::WebMouseEvent mouse; + WebKit::WebMouseWheelEvent wheel; + WebKit::WebTouchEvent touch; + WebKit::WebGestureEvent gesture; + WebKit::WebKeyboardEvent keyboard; + + WebKit::WebInputEvent* web_event = NULL; + if (WebInputEvent::isMouseEventType(web_type)) + web_event = &mouse; + else if (WebInputEvent::isKeyboardEventType(web_type)) + web_event = &keyboard; + else if (WebInputEvent::isTouchEventType(web_type)) + web_event = &touch; + else if (WebInputEvent::isGestureEventType(web_type)) + web_event = &gesture; + else + web_event = &wheel; + web_event->type = web_type; + + return BrowserInputEvent::Create( + NextInputID(), + WebInputEventPayload::Create(*web_event, ui::LatencyInfo(), false), + this); + } + + void QueueEvent(IPC::Message* message) { + queue_->QueueEvent(CreateIPCInputEvent(message)); + } + + void QueueEvent(WebInputEvent::Type web_type) { + queue_->QueueEvent(CreateWebInputEvent(web_type)); + } + + bool Flush(InputEventDisposition disposition) { + StartFlush(); + return FinishFlush(disposition); + } + + void StartFlush() { + acked_dispositions_.clear(); + acked_followup_dispositions_.clear(); + current_packet_id_ = 0; + current_packet_dispositions_.clear(); + queue_->BeginFlush(); + } + + bool FinishFlush(InputEventDisposition disposition) { + if (!current_packet_id_) + return false; + current_packet_dispositions_ = InputEventDispositions( + current_packet_dispositions_.size(), disposition); + return InputQueue::ACK_OK == + queue_->OnEventPacketAck(current_packet_id_, + current_packet_dispositions_); + } + + int64 NextInputID() { return next_input_id_++; } + + scoped_ptr<InputQueue> queue_; + + int routing_id_; + int64 current_packet_id_; + InputEventDispositions current_packet_dispositions_; + + InputEventDispositions acked_dispositions_; + InputEventDispositions acked_followup_dispositions_; + scoped_ptr<BrowserInputEvent> event_to_inject_; + + int num_flush_completions_; + int num_flush_requests_; + int num_packet_deliveries_; + int next_input_id_; +}; + +TEST_F(InputQueueTest, SetNeedsFlushOnQueueEvent) { + EXPECT_EQ(0, num_flush_requests()); + + QueueEvent(WebInputEvent::MouseDown); + EXPECT_EQ(1, num_flush_requests()); + + // Additional queued events should not trigger additional flush requests. + QueueEvent(WebInputEvent::MouseUp); + EXPECT_EQ(1, num_flush_requests()); + QueueEvent(WebInputEvent::TouchStart); + EXPECT_EQ(1, num_flush_requests()); +} + +TEST_F(InputQueueTest, NoSetNeedsFlushOnQueueIfFlushing) { + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + + StartFlush(); + EXPECT_EQ(1, num_flush_requests()); + EXPECT_EQ(1, num_packet_deliveries()); + + // Events queued after a flush will not trigger an additional flush request. + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + QueueEvent(WebInputEvent::GestureScrollEnd); + EXPECT_EQ(1, num_flush_requests()); +} + +TEST_F(InputQueueTest, SetNeedsFlushAfterDidFinishFlushIfEventsQueued) { + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + + StartFlush(); + EXPECT_EQ(1, num_packet_deliveries()); + + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(1, num_flush_requests()); + + // An additional flush request is sent for the event queued after the flush. + ASSERT_TRUE(current_packet_id_); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED)); + EXPECT_EQ(1, num_flush_completions()); + EXPECT_EQ(2, num_flush_requests()); +} + +TEST_F(InputQueueTest, EventPacketSentAfterFlush) { + EXPECT_EQ(0, num_packet_deliveries()); + QueueEvent(WebInputEvent::GestureScrollBegin); + EXPECT_EQ(0, num_packet_deliveries()); + StartFlush(); + EXPECT_EQ(1, num_packet_deliveries()); +} + +TEST_F(InputQueueTest, AcksHandledInProperOrder) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::GestureScrollEnd); + QueueEvent(WebInputEvent::GestureFlingStart); + + queue_->BeginFlush(); + ASSERT_EQ(3u, current_packet_dispositions_.size()); + current_packet_dispositions_[0] = INPUT_EVENT_IMPL_THREAD_CONSUMED; + current_packet_dispositions_[1] = INPUT_EVENT_MAIN_THREAD_CONSUMED; + current_packet_dispositions_[2] = INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS; + queue_->OnEventPacketAck(current_packet_id_, current_packet_dispositions_); + EXPECT_EQ(1, num_flush_completions()); + + ASSERT_EQ(3u, acked_dispositions_.size()); + EXPECT_EQ(acked_dispositions_[0], INPUT_EVENT_IMPL_THREAD_CONSUMED); + EXPECT_EQ(acked_dispositions_[1], INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(acked_dispositions_[2], INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS); +} + +TEST_F(InputQueueTest, FollowupWhenFollowupEventNotConsumed) { + InputEventDisposition unconsumed_dispositions[] = { + INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS, + INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED, + INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS + }; + for (size_t i = 0; i < arraysize(unconsumed_dispositions); ++i) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchStart); + QueueEvent(WebInputEvent::TouchMove); + + Flush(unconsumed_dispositions[i]); + EXPECT_EQ(1u, acked_dispositions_.size()) << i; + EXPECT_EQ(2u, acked_followup_dispositions_.size()) << i; + } +} + +TEST_F(InputQueueTest, NoFollowupWhenFollowupEventConsumed) { + InputEventDisposition consumed_dispositions[] = { + INPUT_EVENT_IMPL_THREAD_CONSUMED, + INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED, + INPUT_EVENT_MAIN_THREAD_CONSUMED + }; + for (size_t i = 0; i < arraysize(consumed_dispositions); ++i) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchStart); + QueueEvent(WebInputEvent::TouchMove); + + Flush(consumed_dispositions[i]); + EXPECT_EQ(3u, acked_dispositions_.size()) << i; + EXPECT_EQ(0u, acked_followup_dispositions_.size()) << i; + } +} + +TEST_F(InputQueueTest, FlushOnEmptyQueueIgnored) { + Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(0, num_flush_requests()); + EXPECT_EQ(0, num_flush_completions()); + + QueueEvent(WebInputEvent::GestureScrollBegin); + Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_requests()); + EXPECT_EQ(1, num_flush_completions()); + + Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_requests()); + EXPECT_EQ(1, num_flush_completions()); +} + +TEST_F(InputQueueTest, FlushContinuesUntilAllEventsProcessed) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::GestureScrollEnd); + QueueEvent(WebInputEvent::GestureFlingStart); + + EXPECT_EQ(1, num_flush_requests()); + Flush(INPUT_EVENT_COULD_NOT_DELIVER); + EXPECT_EQ(0, num_flush_completions()); + + FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER); + EXPECT_EQ(0, num_flush_completions()); + + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED)); + EXPECT_EQ(1, num_flush_completions()); +} + +TEST_F(InputQueueTest, InvalidPacketAckIgnored) { + // Packet never flushed, any ack should be ignored. + InputQueue::AckResult result = + queue_->OnEventPacketAck(0, InputEventDispositions()); + EXPECT_EQ(InputQueue::ACK_UNEXPECTED, result); + + QueueEvent(WebInputEvent::GestureScrollBegin); + StartFlush(); + // Tamper with the sent packet by adding an extra event. + current_packet_dispositions_.push_back(INPUT_EVENT_MAIN_THREAD_CONSUMED); + bool valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(0, num_flush_completions()); + EXPECT_FALSE(valid_packet_ack); + + // Fix the packet. + current_packet_dispositions_.pop_back(); + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_completions()); + EXPECT_TRUE(valid_packet_ack); + + // Tamper with the packet by changing the id. + QueueEvent(WebInputEvent::GestureScrollBegin); + StartFlush(); + int64 packet_ack_id = -1; + std::swap(current_packet_id_, packet_ack_id); + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(1, num_flush_completions()); + EXPECT_FALSE(valid_packet_ack); + + // Fix the packet. + std::swap(current_packet_id_, packet_ack_id); + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED); + EXPECT_EQ(2, num_flush_completions()); + EXPECT_TRUE(valid_packet_ack); +} + +TEST_F(InputQueueTest, InjectedEventsAckedBeforeDidFinishFlush) { + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchMove); + + event_to_inject_ = CreateIPCInputEvent(new InputMsg_Copy(routing_id_)); + Flush(INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS); + EXPECT_EQ(0, num_flush_completions()); + + // The injected event should now be in the event packet. + EXPECT_EQ(1u, current_packet_dispositions_.size()); + EXPECT_EQ(1u, acked_followup_dispositions_.size()); + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED)); + EXPECT_EQ(1, num_flush_completions()); + + QueueEvent(WebInputEvent::GestureScrollBegin); + QueueEvent(WebInputEvent::TouchStart); + event_to_inject_ = CreateWebInputEvent(WebInputEvent::TouchMove); + Flush(INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS); + // |event_to_inject_| is now in the event packet. + EXPECT_EQ(1u, acked_followup_dispositions_.size()); + EXPECT_EQ(1u, current_packet_dispositions_.size()); + + event_to_inject_ = CreateWebInputEvent(WebInputEvent::TouchMove); + // the next |event_to_inject_| is now in the event packet. + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS)); + + EXPECT_EQ(2u, acked_followup_dispositions_.size()); + EXPECT_EQ(1u, current_packet_dispositions_.size()); + EXPECT_EQ(1, num_flush_completions()); + + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED)); + EXPECT_EQ(2, num_flush_completions()); +} + +} // namespace +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router.h b/chromium/content/browser/renderer_host/input/input_router.h index 2d8a4c6dd5d..5e4533fc1dd 100644 --- a/chromium/content/browser/renderer_host/input/input_router.h +++ b/chromium/content/browser/renderer_host/input/input_router.h @@ -24,10 +24,14 @@ class InputRouter : public IPC::Listener { public: virtual ~InputRouter() {} + // Should be called only in response to |SetNeedsFlush| requests made via + // the |InputRouterClient|. + virtual void Flush() = 0; + // Send and take ownership of the the given InputMsg_*. This should be used // only for event types not associated with a WebInputEvent. Returns true on // success and false otherwise. - virtual bool SendInput(IPC::Message* message) = 0; + virtual bool SendInput(scoped_ptr<IPC::Message> message) = 0; // WebInputEvents virtual void SendMouseEvent( @@ -60,9 +64,6 @@ class InputRouter : public IPC::Listener { // |gesture_event| to the router. virtual bool ShouldForwardGestureEvent( const GestureEventWithLatencyInfo& gesture_event) const = 0; - - // Returns |true| if the router has any queued or in-flight gesture events. - virtual bool HasQueuedGestureEvents() const = 0; }; } // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router_client.h b/chromium/content/browser/renderer_host/input/input_router_client.h index 9c854224b52..6479332d444 100644 --- a/chromium/content/browser/renderer_host/input/input_router_client.h +++ b/chromium/content/browser/renderer_host/input/input_router_client.h @@ -61,16 +61,15 @@ class CONTENT_EXPORT InputRouterClient { virtual bool OnSendGestureEventImmediately( const GestureEventWithLatencyInfo& gesture_event) = 0; - // Called upon event ack receipt from the renderer. - virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, - InputEventAckState ack_result) = 0; - virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, - InputEventAckState ack_result) = 0; - virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, - InputEventAckState ack_result) = 0; - virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, - InputEventAckState ack_result) = 0; - virtual void OnUnexpectedEventAck(bool bad_message) = 0; + // Certain router implementations require periodic flushing of queued events. + // When this method is called, the client should ensure a timely call, either + // synchronous or asynchronous, of |Flush| on the InputRouter. + virtual void SetNeedsFlush() = 0; + + // Called when the router has finished flushing all events queued at the time + // of the call to Flush. The call will typically be asynchronous with + // respect to the call to |Flush| on the InputRouter. + virtual void DidFlush() = 0; }; } // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router_unittest.cc b/chromium/content/browser/renderer_host/input/input_router_unittest.cc new file mode 100644 index 00000000000..da1ef1efb70 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_router_unittest.cc @@ -0,0 +1,225 @@ +// Copyright 2013 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/browser/renderer_host/input/input_router_unittest.h" + +#include "content/browser/renderer_host/input/input_router.h" +#include "content/common/input_messages.h" +#include "ui/events/keycodes/keyboard_codes.h" + +#if defined(OS_WIN) || defined(USE_AURA) +#include "content/browser/renderer_host/ui_events_helper.h" +#include "ui/events/event.h" +#endif + +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +InputRouterTest::InputRouterTest() {} +InputRouterTest::~InputRouterTest() {} + +void InputRouterTest::SetUp() { + browser_context_.reset(new TestBrowserContext()); + process_.reset(new MockRenderProcessHost(browser_context_.get())); + client_.reset(new MockInputRouterClient()); + ack_handler_.reset(new MockInputAckHandler()); + input_router_ = CreateInputRouter(process_.get(), + client_.get(), + ack_handler_.get(), + MSG_ROUTING_NONE); + client_->set_input_router(input_router_.get()); + ack_handler_->set_input_router(input_router_.get()); +} + +void InputRouterTest::TearDown() { + // Process all pending tasks to avoid InputRouterTest::leaks. + base::MessageLoop::current()->RunUntilIdle(); + + input_router_.reset(); + client_.reset(); + process_.reset(); + browser_context_.reset(); +} + +void InputRouterTest::SimulateKeyboardEvent(WebInputEvent::Type type) { + NativeWebKeyboardEvent key_event; + key_event.type = type; + key_event.windowsKeyCode = ui::VKEY_L; // non-null made up value. + input_router_->SendKeyboardEvent(key_event, ui::LatencyInfo()); + client_->ExpectSendCalled(true); + EXPECT_EQ(type, client_->sent_key_event().type); + EXPECT_EQ(key_event.windowsKeyCode, + client_->sent_key_event().windowsKeyCode); +} + +void InputRouterTest::SimulateWheelEvent(float dX, + float dY, + int modifiers, + bool precise) { + WebMouseWheelEvent wheel_event; + wheel_event.type = WebInputEvent::MouseWheel; + wheel_event.deltaX = dX; + wheel_event.deltaY = dY; + wheel_event.modifiers = modifiers; + wheel_event.hasPreciseScrollingDeltas = precise; + input_router_->SendWheelEvent( + MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); + client_->ExpectSendCalled(true); + EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); + EXPECT_EQ(dX, client_->sent_wheel_event().event.deltaX); +} + +void InputRouterTest::SimulateMouseMove(int x, int y, int modifiers) { + WebMouseEvent mouse_event; + mouse_event.type = WebInputEvent::MouseMove; + mouse_event.x = mouse_event.windowX = x; + mouse_event.y = mouse_event.windowY = y; + mouse_event.modifiers = modifiers; + input_router_->SendMouseEvent( + MouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo())); + client_->ExpectSendCalled(true); + EXPECT_EQ(mouse_event.type, client_->sent_mouse_event().event.type); + EXPECT_EQ(x, client_->sent_mouse_event().event.x); +} + +void InputRouterTest::SimulateWheelEventWithPhase( + WebMouseWheelEvent::Phase phase) { + WebMouseWheelEvent wheel_event; + wheel_event.type = WebInputEvent::MouseWheel; + wheel_event.phase = phase; + input_router_->SendWheelEvent( + MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo())); + client_->ExpectSendCalled(true); + EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type); + EXPECT_EQ(phase, client_->sent_wheel_event().event.phase); +} + +// Inject provided synthetic WebGestureEvent instance. +void InputRouterTest::SimulateGestureEventCore(WebInputEvent::Type type, + WebGestureEvent::SourceDevice sourceDevice, + WebGestureEvent* gesture_event) { + gesture_event->type = type; + gesture_event->sourceDevice = sourceDevice; + GestureEventWithLatencyInfo gesture_with_latency( + *gesture_event, ui::LatencyInfo()); + input_router_->SendGestureEvent(gesture_with_latency); + client_->ExpectSendCalled(true); + EXPECT_EQ(type, client_->sent_gesture_event().event.type); + EXPECT_EQ(sourceDevice, client_->sent_gesture_event().event.sourceDevice); +} + +// Inject simple synthetic WebGestureEvent instances. +void InputRouterTest::SimulateGestureEvent(WebInputEvent::Type type, + WebGestureEvent::SourceDevice sourceDevice) { + WebGestureEvent gesture_event; + SimulateGestureEventCore(type, sourceDevice, &gesture_event); +} + +void InputRouterTest::SimulateGestureScrollUpdateEvent(float dX, + float dY, + int modifiers) { + WebGestureEvent gesture_event; + gesture_event.data.scrollUpdate.deltaX = dX; + gesture_event.data.scrollUpdate.deltaY = dY; + gesture_event.modifiers = modifiers; + SimulateGestureEventCore(WebInputEvent::GestureScrollUpdate, + WebGestureEvent::Touchscreen, &gesture_event); +} + +void InputRouterTest::SimulateGesturePinchUpdateEvent(float scale, + float anchorX, + float anchorY, + int modifiers) { + WebGestureEvent gesture_event; + gesture_event.data.pinchUpdate.scale = scale; + gesture_event.x = anchorX; + gesture_event.y = anchorY; + gesture_event.modifiers = modifiers; + SimulateGestureEventCore(WebInputEvent::GesturePinchUpdate, + WebGestureEvent::Touchscreen, &gesture_event); +} + +// Inject synthetic GestureFlingStart events. +void InputRouterTest::SimulateGestureFlingStartEvent( + float velocityX, + float velocityY, + WebGestureEvent::SourceDevice sourceDevice) { + WebGestureEvent gesture_event; + gesture_event.data.flingStart.velocityX = velocityX; + gesture_event.data.flingStart.velocityY = velocityY; + SimulateGestureEventCore(WebInputEvent::GestureFlingStart, sourceDevice, + &gesture_event); +} + +void InputRouterTest::SimulateTouchEvent( + int x, + int y) { + PressTouchPoint(x, y); + SendTouchEvent(); +} + +// Set the timestamp for the touch-event. +void InputRouterTest::SetTouchTimestamp(base::TimeDelta timestamp) { + touch_event_.timeStampSeconds = timestamp.InSecondsF(); +} + +// Sends a touch event (irrespective of whether the page has a touch-event +// handler or not). +void InputRouterTest::SendTouchEvent() { + input_router_->SendTouchEvent( + TouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo())); + + // Mark all the points as stationary. And remove the points that have been + // released. + int point = 0; + for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) { + if (touch_event_.touches[i].state == WebTouchPoint::StateReleased) + continue; + + touch_event_.touches[point] = touch_event_.touches[i]; + touch_event_.touches[point].state = + WebTouchPoint::StateStationary; + ++point; + } + touch_event_.touchesLength = point; + touch_event_.type = WebInputEvent::Undefined; +} + +int InputRouterTest::PressTouchPoint(int x, int y) { + if (touch_event_.touchesLength == touch_event_.touchesLengthCap) + return -1; + WebTouchPoint& point = + touch_event_.touches[touch_event_.touchesLength]; + point.id = touch_event_.touchesLength; + point.position.x = point.screenPosition.x = x; + point.position.y = point.screenPosition.y = y; + point.state = WebTouchPoint::StatePressed; + point.radiusX = point.radiusY = 1.f; + ++touch_event_.touchesLength; + touch_event_.type = WebInputEvent::TouchStart; + return point.id; +} + +void InputRouterTest::MoveTouchPoint(int index, int x, int y) { + CHECK(index >= 0 && index < touch_event_.touchesLengthCap); + WebTouchPoint& point = touch_event_.touches[index]; + point.position.x = point.screenPosition.x = x; + point.position.y = point.screenPosition.y = y; + touch_event_.touches[index].state = WebTouchPoint::StateMoved; + touch_event_.type = WebInputEvent::TouchMove; +} + +void InputRouterTest::ReleaseTouchPoint(int index) { + CHECK(index >= 0 && index < touch_event_.touchesLengthCap); + touch_event_.touches[index].state = WebTouchPoint::StateReleased; + touch_event_.type = WebInputEvent::TouchEnd; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/input_router_unittest.h b/chromium/content/browser/renderer_host/input/input_router_unittest.h new file mode 100644 index 00000000000..20a68af3fb1 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/input_router_unittest.h @@ -0,0 +1,83 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_UNITTEST_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_UNITTEST_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/input_router_client.h" +#include "content/browser/renderer_host/input/mock_input_ack_handler.h" +#include "content/browser/renderer_host/input/mock_input_router_client.h" +#include "content/public/test/mock_render_process_host.h" +#include "content/public/test/test_browser_context.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +namespace content { + +class InputRouter; +class MockInputRouterClient; + +class InputRouterTest : public testing::Test { + public: + InputRouterTest(); + virtual ~InputRouterTest(); + + protected: + // Called on SetUp. + virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process, + InputRouterClient* client, + InputAckHandler* handler, + int routing_id) = 0; + + // testing::Test + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + void SendInputEventACK(WebKit::WebInputEvent::Type type, + InputEventAckState ack_result); + void SimulateKeyboardEvent(WebKit::WebInputEvent::Type type); + void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise); + void SimulateMouseMove(int x, int y, int modifiers); + void SimulateWheelEventWithPhase(WebKit::WebMouseWheelEvent::Phase phase); + void SimulateGestureEventCore(WebKit::WebInputEvent::Type type, + WebKit::WebGestureEvent::SourceDevice sourceDevice, + WebKit::WebGestureEvent* gesture_event); + void SimulateGestureEvent(WebKit::WebInputEvent::Type type, + WebKit::WebGestureEvent::SourceDevice sourceDevice); + void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers); + void SimulateGesturePinchUpdateEvent(float scale, + float anchorX, + float anchorY, + int modifiers); + void SimulateGestureFlingStartEvent( + float velocityX, + float velocityY, + WebKit::WebGestureEvent::SourceDevice sourceDevice); + void SimulateTouchEvent(int x, int y); + void SetTouchTimestamp(base::TimeDelta timestamp); + + // Sends a touch event (irrespective of whether the page has a touch-event + // handler or not). + void SendTouchEvent(); + + int PressTouchPoint(int x, int y); + void MoveTouchPoint(int index, int x, int y); + void ReleaseTouchPoint(int index); + + scoped_ptr<MockRenderProcessHost> process_; + scoped_ptr<MockInputRouterClient> client_; + scoped_ptr<MockInputAckHandler> ack_handler_; + scoped_ptr<InputRouter> input_router_; + + private: + base::MessageLoopForUI message_loop_; + WebKit::WebTouchEvent touch_event_; + + scoped_ptr<TestBrowserContext> browser_context_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_UNITTEST_H_ diff --git a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc new file mode 100644 index 00000000000..fc97ab3450b --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.cc @@ -0,0 +1,77 @@ +// Copyright 2013 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/browser/renderer_host/input/mock_input_ack_handler.h" + +#include "content/browser/renderer_host/input/input_router.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +MockInputAckHandler::MockInputAckHandler() + : input_router_(NULL), + ack_count_(0), + unexpected_event_ack_called_(false), + ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {} + +MockInputAckHandler::~MockInputAckHandler() {} + +void MockInputAckHandler::OnKeyboardEventAck( + const NativeWebKeyboardEvent& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_key_event_ = event; + RecordAckCalled(ack_result); +} + +void MockInputAckHandler::OnWheelEventAck( + const WebMouseWheelEvent& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_wheel_event_ = event; + RecordAckCalled(ack_result); +} + +void MockInputAckHandler::OnTouchEventAck( + const TouchEventWithLatencyInfo& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_touch_event_ = event; + RecordAckCalled(ack_result); + if (touch_followup_event_) + input_router_->SendGestureEvent(*touch_followup_event_); +} + +void MockInputAckHandler::OnGestureEventAck( + const WebGestureEvent& event, + InputEventAckState ack_result) { + VLOG(1) << __FUNCTION__ << " called!"; + acked_gesture_event_ = event; + RecordAckCalled(ack_result); +} + +void MockInputAckHandler::OnUnexpectedEventAck(UnexpectedEventAckType type) { + VLOG(1) << __FUNCTION__ << " called!"; + unexpected_event_ack_called_ = true; +} + +void MockInputAckHandler::ExpectAckCalled(int times) { + EXPECT_EQ(times, ack_count_); + ack_count_ = 0; +} + +void MockInputAckHandler::RecordAckCalled(InputEventAckState ack_result) { + ++ack_count_; + ack_state_ = ack_result; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h new file mode 100644 index 00000000000..604b26b94cb --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_ack_handler.h @@ -0,0 +1,77 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" + +namespace content { + +class InputRouter; + +class MockInputAckHandler : public InputAckHandler { + public: + MockInputAckHandler(); + virtual ~MockInputAckHandler(); + + // InputAckHandler + virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, + InputEventAckState ack_result) OVERRIDE; + virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE; + + void ExpectAckCalled(int times); + + void set_input_router(InputRouter* input_router) { + input_router_ = input_router; + } + + void set_followup_touch_event(scoped_ptr<GestureEventWithLatencyInfo> event) { + touch_followup_event_ = event.Pass(); + } + + bool unexpected_event_ack_called() const { + return unexpected_event_ack_called_; + } + InputEventAckState ack_state() const { return ack_state_; } + + const NativeWebKeyboardEvent& acked_keyboard_event() const { + return acked_key_event_; + } + const WebKit::WebMouseWheelEvent& acked_wheel_event() const { + return acked_wheel_event_; + } + const TouchEventWithLatencyInfo& acked_touch_event() const { + return acked_touch_event_; + } + const WebKit::WebGestureEvent& acked_gesture_event() const { + return acked_gesture_event_; + } + + private: + void RecordAckCalled(InputEventAckState ack_result); + + InputRouter* input_router_; + + int ack_count_; + bool unexpected_event_ack_called_; + InputEventAckState ack_state_; + NativeWebKeyboardEvent acked_key_event_; + WebKit::WebMouseWheelEvent acked_wheel_event_; + TouchEventWithLatencyInfo acked_touch_event_; + WebKit::WebGestureEvent acked_gesture_event_; + + scoped_ptr<GestureEventWithLatencyInfo> touch_followup_event_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_ diff --git a/chromium/content/browser/renderer_host/input/mock_input_router_client.cc b/chromium/content/browser/renderer_host/input/mock_input_router_client.cc new file mode 100644 index 00000000000..86fb53b6b58 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_router_client.cc @@ -0,0 +1,148 @@ +// Copyright 2013 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/browser/renderer_host/input/mock_input_router_client.h" + +#include "content/browser/renderer_host/input/input_router.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; +using WebKit::WebGestureEvent; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; + +namespace content { + +MockInputRouterClient::MockInputRouterClient() + : input_router_(NULL), + in_flight_event_count_(0), + has_touch_handler_(false), + filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED), + is_shortcut_(false), + allow_send_event_(true), + send_called_(false), + send_immediately_called_(false), + did_flush_called_(false), + set_needs_flush_called_(false) {} + +MockInputRouterClient::~MockInputRouterClient() {} + +InputEventAckState MockInputRouterClient::FilterInputEvent( + const WebInputEvent& input_event, + const ui::LatencyInfo& latency_info) { + return filter_state_; +} + +void MockInputRouterClient::IncrementInFlightEventCount() { + ++in_flight_event_count_; +} + +void MockInputRouterClient::DecrementInFlightEventCount() { + --in_flight_event_count_; +} + +void MockInputRouterClient::OnHasTouchEventHandlers( + bool has_handlers) { + has_touch_handler_ = has_handlers; +} + +bool MockInputRouterClient::OnSendKeyboardEvent( + const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info, + bool* is_shortcut) { + send_called_ = true; + sent_key_event_ = key_event; + *is_shortcut = is_shortcut_; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) { + send_called_ = true; + sent_wheel_event_ = wheel_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) { + send_called_ = true; + sent_mouse_event_ = mouse_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) { + send_called_ = true; + sent_touch_event_ = touch_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) { + send_called_ = true; + sent_gesture_event_ = gesture_event; + + return allow_send_event_ && + input_router_->ShouldForwardGestureEvent(gesture_event); +} + +bool MockInputRouterClient::OnSendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) { + send_immediately_called_ = true; + immediately_sent_mouse_event_ = mouse_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) { + send_immediately_called_ = true; + immediately_sent_touch_event_ = touch_event; + + return allow_send_event_; +} + +bool MockInputRouterClient::OnSendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) { + send_immediately_called_ = true; + immediately_sent_gesture_event_ = gesture_event; + return allow_send_event_; +} + +void MockInputRouterClient::ExpectSendCalled(bool called) { + EXPECT_EQ(called, send_called_); + send_called_ = false; +} + +void MockInputRouterClient::ExpectSendImmediatelyCalled(bool called) { + EXPECT_EQ(called, send_immediately_called_); + send_immediately_called_ = false; +} + +void MockInputRouterClient::ExpectNeedsFlushCalled(bool called) { + EXPECT_EQ(called, set_needs_flush_called_); + set_needs_flush_called_ = false; +} + +void MockInputRouterClient::ExpectDidFlushCalled(bool called) { + EXPECT_EQ(called, did_flush_called_); + did_flush_called_ = false; +} + +void MockInputRouterClient::DidFlush() { + did_flush_called_ = true; +} + +void MockInputRouterClient::SetNeedsFlush() { + set_needs_flush_called_ = true; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/mock_input_router_client.h b/chromium/content/browser/renderer_host/input/mock_input_router_client.h new file mode 100644 index 00000000000..a02f73891f0 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/mock_input_router_client.h @@ -0,0 +1,126 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/input/input_router_client.h" + +namespace content { + +class InputRouter; + +class MockInputRouterClient : public InputRouterClient { + public: + MockInputRouterClient(); + virtual ~MockInputRouterClient(); + + // InputRouterClient + virtual InputEventAckState FilterInputEvent( + const WebKit::WebInputEvent& input_event, + const ui::LatencyInfo& latency_info) OVERRIDE; + virtual void IncrementInFlightEventCount() OVERRIDE; + virtual void DecrementInFlightEventCount() OVERRIDE; + virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE; + virtual bool OnSendKeyboardEvent( + const NativeWebKeyboardEvent& key_event, + const ui::LatencyInfo& latency_info, + bool* is_shortcut) OVERRIDE; + virtual bool OnSendWheelEvent( + const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE; + virtual bool OnSendMouseEvent( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual bool OnSendTouchEvent( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual bool OnSendGestureEvent( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual bool OnSendMouseEventImmediately( + const MouseEventWithLatencyInfo& mouse_event) OVERRIDE; + virtual bool OnSendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch_event) OVERRIDE; + virtual bool OnSendGestureEventImmediately( + const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual void DidFlush() OVERRIDE; + virtual void SetNeedsFlush() OVERRIDE; + + void ExpectSendCalled(bool called); + void ExpectSendImmediatelyCalled(bool called); + void ExpectNeedsFlushCalled(bool called); + void ExpectDidFlushCalled(bool called); + + void set_input_router(InputRouter* input_router) { + input_router_ = input_router; + } + + bool has_touch_handler() const { return has_touch_handler_; } + void set_filter_state(InputEventAckState filter_state) { + filter_state_ = filter_state; + } + int in_flight_event_count() const { + return in_flight_event_count_; + } + void set_is_shortcut(bool is_shortcut) { + is_shortcut_ = is_shortcut; + } + void set_allow_send_event(bool allow) { + allow_send_event_ = allow; + } + const NativeWebKeyboardEvent& sent_key_event() { + return sent_key_event_; + } + const MouseWheelEventWithLatencyInfo& sent_wheel_event() { + return sent_wheel_event_; + } + const MouseEventWithLatencyInfo& sent_mouse_event() { + return sent_mouse_event_; + } + const GestureEventWithLatencyInfo& sent_gesture_event() { + return sent_gesture_event_; + } + const MouseEventWithLatencyInfo& immediately_sent_mouse_event() { + return immediately_sent_mouse_event_; + } + const TouchEventWithLatencyInfo& immediately_sent_touch_event() { + return immediately_sent_touch_event_; + } + const GestureEventWithLatencyInfo& immediately_sent_gesture_event() { + return immediately_sent_gesture_event_; + } + + bool did_flush_called() const { return did_flush_called_; } + bool needs_flush_called() const { return set_needs_flush_called_; } + void set_followup_touch_event(scoped_ptr<GestureEventWithLatencyInfo> event) { + touch_followup_event_ = event.Pass(); + } + + private: + InputRouter* input_router_; + int in_flight_event_count_; + bool has_touch_handler_; + + InputEventAckState filter_state_; + + bool is_shortcut_; + bool allow_send_event_; + bool send_called_; + NativeWebKeyboardEvent sent_key_event_; + MouseWheelEventWithLatencyInfo sent_wheel_event_; + MouseEventWithLatencyInfo sent_mouse_event_; + TouchEventWithLatencyInfo sent_touch_event_; + GestureEventWithLatencyInfo sent_gesture_event_; + + bool send_immediately_called_; + MouseEventWithLatencyInfo immediately_sent_mouse_event_; + TouchEventWithLatencyInfo immediately_sent_touch_event_; + GestureEventWithLatencyInfo immediately_sent_gesture_event_; + + bool did_flush_called_; + bool set_needs_flush_called_; + scoped_ptr<GestureEventWithLatencyInfo> touch_followup_event_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_ diff --git a/chromium/content/browser/renderer_host/input/touch_event_queue.cc b/chromium/content/browser/renderer_host/input/touch_event_queue.cc index c1675b9952d..e22c05d0f80 100644 --- a/chromium/content/browser/renderer_host/input/touch_event_queue.cc +++ b/chromium/content/browser/renderer_host/input/touch_event_queue.cc @@ -91,7 +91,8 @@ class CoalescedWebTouchEvent { TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client) : client_(client), - dispatching_touch_ack_(false) { + dispatching_touch_ack_(false), + no_touch_move_to_renderer_(false) { DCHECK(client); } @@ -212,6 +213,10 @@ bool TouchEventQueue::ShouldForwardToRenderer( if (event.type == WebKit::WebInputEvent::TouchStart) return true; + if (event.type == WebKit::WebInputEvent::TouchMove && + no_touch_move_to_renderer_) + return false; + for (unsigned int i = 0; i < event.touchesLength; ++i) { const WebKit::WebTouchPoint& point = event.touches[i]; // If a point has been stationary, then don't take it into account. diff --git a/chromium/content/browser/renderer_host/input/touch_event_queue.h b/chromium/content/browser/renderer_host/input/touch_event_queue.h index 23dda66c38b..358fae9c50d 100644 --- a/chromium/content/browser/renderer_host/input/touch_event_queue.h +++ b/chromium/content/browser/renderer_host/input/touch_event_queue.h @@ -61,6 +61,10 @@ class TouchEventQueue { return touch_queue_.empty(); } + void set_no_touch_move_to_renderer(bool value) { + no_touch_move_to_renderer_ = value; + } + private: friend class MockRenderWidgetHost; friend class ImmediateInputRouterTest; @@ -88,6 +92,11 @@ class TouchEventQueue { // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|. bool dispatching_touch_ack_; + // Don't send touch move events to renderer. This is enabled when the page + // is scrolling. This behaviour is currently enabled only on aura behind a + // flag. + bool no_touch_move_to_renderer_; + DISALLOW_COPY_AND_ASSIGN(TouchEventQueue); }; diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc index eca820bb28b..b5c96c657eb 100644 --- a/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_android.cc @@ -7,8 +7,8 @@ #include "base/logging.h" #include "content/browser/renderer_host/input/web_input_event_util.h" #include "content/browser/renderer_host/input/web_input_event_util_posix.h" -#include "ui/base/keycodes/keyboard_code_conversion_android.h" -#include "ui/base/keycodes/keyboard_codes_posix.h" +#include "ui/events/keycodes/keyboard_code_conversion_android.h" +#include "ui/events/keycodes/keyboard_codes_posix.h" namespace content { diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc new file mode 100644 index 00000000000..c101595c79f --- /dev/null +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.cc @@ -0,0 +1,603 @@ +// Copyright 2013 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/browser/renderer_host/input/web_input_event_builders_gtk.h" + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include "base/logging.h" +#include "content/browser/renderer_host/input/web_input_event_util_posix.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/keycodes/keyboard_code_conversion_gtk.h" + +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebKeyboardEvent; + +namespace { + +// For click count tracking. +static int num_clicks = 0; +static GdkWindow* last_click_event_window = 0; +static gint last_click_time = 0; +static gint last_click_x = 0; +static gint last_click_y = 0; +static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonNone; + +bool ShouldForgetPreviousClick(GdkWindow* window, gint time, gint x, gint y) { + static GtkSettings* settings = gtk_settings_get_default(); + + if (window != last_click_event_window) + return true; + + gint double_click_time = 250; + gint double_click_distance = 5; + g_object_get(G_OBJECT(settings), + "gtk-double-click-time", + &double_click_time, + "gtk-double-click-distance", + &double_click_distance, + NULL); + return (time - last_click_time) > double_click_time || + std::abs(x - last_click_x) > double_click_distance || + std::abs(y - last_click_y) > double_click_distance; +} + +void ResetClickCountState() { + num_clicks = 0; + last_click_event_window = 0; + last_click_time = 0; + last_click_x = 0; + last_click_y = 0; + last_click_button = WebKit::WebMouseEvent::ButtonNone; +} + +bool IsKeyPadKeyval(guint keyval) { + // Keypad keyvals all fall into one range. + return keyval >= GDK_KP_Space && keyval <= GDK_KP_9; +} + +double GdkEventTimeToWebEventTime(guint32 time) { + // Convert from time in ms to time in sec. + return time / 1000.0; +} + +int GdkStateToWebEventModifiers(guint state) { + int modifiers = 0; + if (state & GDK_SHIFT_MASK) + modifiers |= WebInputEvent::ShiftKey; + if (state & GDK_CONTROL_MASK) + modifiers |= WebInputEvent::ControlKey; + if (state & GDK_MOD1_MASK) + modifiers |= WebInputEvent::AltKey; + if (state & GDK_META_MASK) + modifiers |= WebInputEvent::MetaKey; + if (state & GDK_BUTTON1_MASK) + modifiers |= WebInputEvent::LeftButtonDown; + if (state & GDK_BUTTON2_MASK) + modifiers |= WebInputEvent::MiddleButtonDown; + if (state & GDK_BUTTON3_MASK) + modifiers |= WebInputEvent::RightButtonDown; + if (state & GDK_LOCK_MASK) + modifiers |= WebInputEvent::CapsLockOn; + if (state & GDK_MOD2_MASK) + modifiers |= WebInputEvent::NumLockOn; + return modifiers; +} + +ui::KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) { + static const unsigned int kHardwareCodeToGDKKeyval[] = { + 0, // 0x00: + 0, // 0x01: + 0, // 0x02: + 0, // 0x03: + 0, // 0x04: + 0, // 0x05: + 0, // 0x06: + 0, // 0x07: + 0, // 0x08: + 0, // 0x09: GDK_Escape + GDK_1, // 0x0A: GDK_1 + GDK_2, // 0x0B: GDK_2 + GDK_3, // 0x0C: GDK_3 + GDK_4, // 0x0D: GDK_4 + GDK_5, // 0x0E: GDK_5 + GDK_6, // 0x0F: GDK_6 + GDK_7, // 0x10: GDK_7 + GDK_8, // 0x11: GDK_8 + GDK_9, // 0x12: GDK_9 + GDK_0, // 0x13: GDK_0 + GDK_minus, // 0x14: GDK_minus + GDK_equal, // 0x15: GDK_equal + 0, // 0x16: GDK_BackSpace + 0, // 0x17: GDK_Tab + GDK_q, // 0x18: GDK_q + GDK_w, // 0x19: GDK_w + GDK_e, // 0x1A: GDK_e + GDK_r, // 0x1B: GDK_r + GDK_t, // 0x1C: GDK_t + GDK_y, // 0x1D: GDK_y + GDK_u, // 0x1E: GDK_u + GDK_i, // 0x1F: GDK_i + GDK_o, // 0x20: GDK_o + GDK_p, // 0x21: GDK_p + GDK_bracketleft, // 0x22: GDK_bracketleft + GDK_bracketright, // 0x23: GDK_bracketright + 0, // 0x24: GDK_Return + 0, // 0x25: GDK_Control_L + GDK_a, // 0x26: GDK_a + GDK_s, // 0x27: GDK_s + GDK_d, // 0x28: GDK_d + GDK_f, // 0x29: GDK_f + GDK_g, // 0x2A: GDK_g + GDK_h, // 0x2B: GDK_h + GDK_j, // 0x2C: GDK_j + GDK_k, // 0x2D: GDK_k + GDK_l, // 0x2E: GDK_l + GDK_semicolon, // 0x2F: GDK_semicolon + GDK_apostrophe, // 0x30: GDK_apostrophe + GDK_grave, // 0x31: GDK_grave + 0, // 0x32: GDK_Shift_L + GDK_backslash, // 0x33: GDK_backslash + GDK_z, // 0x34: GDK_z + GDK_x, // 0x35: GDK_x + GDK_c, // 0x36: GDK_c + GDK_v, // 0x37: GDK_v + GDK_b, // 0x38: GDK_b + GDK_n, // 0x39: GDK_n + GDK_m, // 0x3A: GDK_m + GDK_comma, // 0x3B: GDK_comma + GDK_period, // 0x3C: GDK_period + GDK_slash, // 0x3D: GDK_slash + 0, // 0x3E: GDK_Shift_R + 0, // 0x3F: + 0, // 0x40: + 0, // 0x41: + 0, // 0x42: + 0, // 0x43: + 0, // 0x44: + 0, // 0x45: + 0, // 0x46: + 0, // 0x47: + 0, // 0x48: + 0, // 0x49: + 0, // 0x4A: + 0, // 0x4B: + 0, // 0x4C: + 0, // 0x4D: + 0, // 0x4E: + 0, // 0x4F: + 0, // 0x50: + 0, // 0x51: + 0, // 0x52: + 0, // 0x53: + 0, // 0x54: + 0, // 0x55: + 0, // 0x56: + 0, // 0x57: + 0, // 0x58: + 0, // 0x59: + 0, // 0x5A: + 0, // 0x5B: + 0, // 0x5C: + 0, // 0x5D: + 0, // 0x5E: + 0, // 0x5F: + 0, // 0x60: + 0, // 0x61: + 0, // 0x62: + 0, // 0x63: + 0, // 0x64: + 0, // 0x65: + 0, // 0x66: + 0, // 0x67: + 0, // 0x68: + 0, // 0x69: + 0, // 0x6A: + 0, // 0x6B: + 0, // 0x6C: + 0, // 0x6D: + 0, // 0x6E: + 0, // 0x6F: + 0, // 0x70: + 0, // 0x71: + 0, // 0x72: + GDK_Super_L, // 0x73: GDK_Super_L + GDK_Super_R, // 0x74: GDK_Super_R + }; + + // |windows_key_code| has to include a valid virtual-key code even when we + // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard + // on the Hebrew layout, |windows_key_code| should be VK_A. + // On the other hand, |event->keyval| value depends on the current + // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on + // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this + // ui::WindowsKeyCodeForGdkKeyCode() call returns 0. + // To improve compatibilty with Windows, we use |event->hardware_keycode| + // for retrieving its Windows key-code for the keys when the + // WebCore::windows_key_codeForEvent() call returns 0. + // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap + // objects cannot change because |event->hardware_keycode| doesn't change + // even when we change the layout options, e.g. when we swap a control + // key and a caps-lock key, GTK doesn't swap their + // |event->hardware_keycode| values but swap their |event->keyval| values. + ui::KeyboardCode windows_key_code = + ui::WindowsKeyCodeForGdkKeyCode(event->keyval); + if (windows_key_code) + return windows_key_code; + + if (event->hardware_keycode < arraysize(kHardwareCodeToGDKKeyval)) { + int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode]; + if (keyval) + return ui::WindowsKeyCodeForGdkKeyCode(keyval); + } + + // This key is one that keyboard-layout drivers cannot change. + // Use |event->keyval| to retrieve its |windows_key_code| value. + return ui::WindowsKeyCodeForGdkKeyCode(event->keyval); +} + +// Normalizes event->state to make it Windows/Mac compatible. Since the way +// of setting modifier mask on X is very different than Windows/Mac as shown +// in http://crbug.com/127142#c8, the normalization is necessary. +guint NormalizeEventState(const GdkEventKey* event) { + guint mask = 0; + switch (GdkEventToWindowsKeyCode(event)) { + case ui::VKEY_CONTROL: + case ui::VKEY_LCONTROL: + case ui::VKEY_RCONTROL: + mask = GDK_CONTROL_MASK; + break; + case ui::VKEY_SHIFT: + case ui::VKEY_LSHIFT: + case ui::VKEY_RSHIFT: + mask = GDK_SHIFT_MASK; + break; + case ui::VKEY_MENU: + case ui::VKEY_LMENU: + case ui::VKEY_RMENU: + mask = GDK_MOD1_MASK; + break; + case ui::VKEY_CAPITAL: + mask = GDK_LOCK_MASK; + break; + default: + return event->state; + } + if (event->type == GDK_KEY_PRESS) + return event->state | mask; + return event->state & ~mask; +} + +// Gets the corresponding control character of a specified key code. See: +// http://en.wikipedia.org/wiki/Control_characters +// We emulate Windows behavior here. +int GetControlCharacter(ui::KeyboardCode windows_key_code, bool shift) { + if (windows_key_code >= ui::VKEY_A && windows_key_code <= ui::VKEY_Z) { + // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A + return windows_key_code - ui::VKEY_A + 1; + } + if (shift) { + // following graphics chars require shift key to input. + switch (windows_key_code) { + // ctrl-@ maps to \x00 (Null byte) + case ui::VKEY_2: + return 0; + // ctrl-^ maps to \x1E (Record separator, Information separator two) + case ui::VKEY_6: + return 0x1E; + // ctrl-_ maps to \x1F (Unit separator, Information separator one) + case ui::VKEY_OEM_MINUS: + return 0x1F; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + return 0; + } + } else { + switch (windows_key_code) { + // ctrl-[ maps to \x1B (Escape) + case ui::VKEY_OEM_4: + return 0x1B; + // ctrl-\ maps to \x1C (File separator, Information separator four) + case ui::VKEY_OEM_5: + return 0x1C; + // ctrl-] maps to \x1D (Group separator, Information separator three) + case ui::VKEY_OEM_6: + return 0x1D; + // ctrl-Enter maps to \x0A (Line feed) + case ui::VKEY_RETURN: + return 0x0A; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + return 0; + } + } +} + +} // namespace + +namespace content { + +// WebKeyboardEvent ----------------------------------------------------------- + +WebKeyboardEvent WebKeyboardEventBuilder::Build(const GdkEventKey* event) { + WebKeyboardEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(NormalizeEventState(event)); + + switch (event->type) { + case GDK_KEY_RELEASE: + result.type = WebInputEvent::KeyUp; + break; + case GDK_KEY_PRESS: + result.type = WebInputEvent::RawKeyDown; + break; + default: + NOTREACHED(); + } + + // According to MSDN: + // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx + // Key events with Alt modifier and F10 are system key events. + // We just emulate this behavior. It's necessary to prevent webkit from + // processing keypress event generated by alt-d, etc. + // F10 is not special on Linux, so don't treat it as system key. + if (result.modifiers & WebInputEvent::AltKey) + result.isSystemKey = true; + + // The key code tells us which physical key was pressed (for example, the + // A key went down or up). It does not determine whether A should be lower + // or upper case. This is what text does, which should be the keyval. + ui::KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event); + result.windowsKeyCode = GetWindowsKeyCodeWithoutLocation(windows_key_code); + result.modifiers |= GetLocationModifiersFromWindowsKeyCode(windows_key_code); + result.nativeKeyCode = event->hardware_keycode; + + if (result.windowsKeyCode == ui::VKEY_RETURN) { + // We need to treat the enter key as a key press of character \r. This + // is apparently just how webkit handles it and what it expects. + result.unmodifiedText[0] = '\r'; + } else { + // FIXME: fix for non BMP chars + result.unmodifiedText[0] = + static_cast<int>(gdk_keyval_to_unicode(event->keyval)); + } + + // If ctrl key is pressed down, then control character shall be input. + if (result.modifiers & WebInputEvent::ControlKey) { + result.text[0] = + GetControlCharacter(ui::KeyboardCode(result.windowsKeyCode), + result.modifiers & WebInputEvent::ShiftKey); + } else { + result.text[0] = result.unmodifiedText[0]; + } + + result.setKeyIdentifierFromWindowsKeyCode(); + + // FIXME: Do we need to set IsAutoRepeat? + if (IsKeyPadKeyval(event->keyval)) + result.modifiers |= WebInputEvent::IsKeyPad; + + return result; +} + +WebKeyboardEvent WebKeyboardEventBuilder::Build(wchar_t character, + int state, + double timeStampSeconds) { + // keyboardEvent(const GdkEventKey*) depends on the GdkEventKey object and + // it is hard to use/ it from signal handlers which don't use GdkEventKey + // objects (e.g. GtkIMContext signal handlers.) For such handlers, this + // function creates a WebInputEvent::Char event without using a + // GdkEventKey object. + WebKeyboardEvent result; + result.type = WebKit::WebInputEvent::Char; + result.timeStampSeconds = timeStampSeconds; + result.modifiers = GdkStateToWebEventModifiers(state); + result.windowsKeyCode = character; + result.nativeKeyCode = character; + result.text[0] = character; + result.unmodifiedText[0] = character; + + // According to MSDN: + // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx + // Key events with Alt modifier and F10 are system key events. + // We just emulate this behavior. It's necessary to prevent webkit from + // processing keypress event generated by alt-d, etc. + // F10 is not special on Linux, so don't treat it as system key. + if (result.modifiers & WebInputEvent::AltKey) + result.isSystemKey = true; + + return result; +} + +// WebMouseEvent -------------------------------------------------------------- + +WebMouseEvent WebMouseEventBuilder::Build(const GdkEventButton* event) { + WebMouseEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + result.clickCount = 0; + + switch (event->type) { + case GDK_BUTTON_PRESS: + result.type = WebInputEvent::MouseDown; + break; + case GDK_BUTTON_RELEASE: + result.type = WebInputEvent::MouseUp; + break; + case GDK_3BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + default: + NOTREACHED(); + } + + result.button = WebMouseEvent::ButtonNone; + if (event->button == 1) + result.button = WebMouseEvent::ButtonLeft; + else if (event->button == 2) + result.button = WebMouseEvent::ButtonMiddle; + else if (event->button == 3) + result.button = WebMouseEvent::ButtonRight; + + if (result.type == WebInputEvent::MouseDown) { + bool forgetPreviousClick = ShouldForgetPreviousClick( + event->window, event->time, event->x, event->y); + + if (!forgetPreviousClick && result.button == last_click_button) { + ++num_clicks; + } else { + num_clicks = 1; + + last_click_event_window = event->window; + last_click_x = event->x; + last_click_y = event->y; + last_click_button = result.button; + } + last_click_time = event->time; + } + result.clickCount = num_clicks; + + return result; +} + +WebMouseEvent WebMouseEventBuilder::Build(const GdkEventMotion* event) { + WebMouseEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + + switch (event->type) { + case GDK_MOTION_NOTIFY: + result.type = WebInputEvent::MouseMove; + break; + default: + NOTREACHED(); + } + + result.button = WebMouseEvent::ButtonNone; + if (event->state & GDK_BUTTON1_MASK) + result.button = WebMouseEvent::ButtonLeft; + else if (event->state & GDK_BUTTON2_MASK) + result.button = WebMouseEvent::ButtonMiddle; + else if (event->state & GDK_BUTTON3_MASK) + result.button = WebMouseEvent::ButtonRight; + + if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y)) + ResetClickCountState(); + + return result; +} + +WebMouseEvent WebMouseEventBuilder::Build(const GdkEventCrossing* event) { + WebMouseEvent result; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + + switch (event->type) { + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + // Note that if we sent MouseEnter or MouseLeave to WebKit, it + // wouldn't work - they don't result in the proper JavaScript events. + // MouseMove does the right thing. + result.type = WebInputEvent::MouseMove; + break; + default: + NOTREACHED(); + } + + result.button = WebMouseEvent::ButtonNone; + if (event->state & GDK_BUTTON1_MASK) + result.button = WebMouseEvent::ButtonLeft; + else if (event->state & GDK_BUTTON2_MASK) + result.button = WebMouseEvent::ButtonMiddle; + else if (event->state & GDK_BUTTON3_MASK) + result.button = WebMouseEvent::ButtonRight; + + if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y)) + ResetClickCountState(); + + return result; +} + +// WebMouseWheelEvent --------------------------------------------------------- + +float WebMouseWheelEventBuilder::ScrollbarPixelsPerTick() { + // How much should we scroll per mouse wheel event? + // - Windows uses 3 lines by default and obeys a system setting. + // - Mozilla has a pref that lets you either use the "system" number of lines + // to scroll, or lets the user override it. + // For the "system" number of lines, it appears they've hardcoded 3. + // See case NS_MOUSE_SCROLL in content/events/src/nsEventStateManager.cpp + // and InitMouseScrollEvent in widget/src/gtk2/nsCommonWidget.cpp . + // - Gtk makes the scroll amount a function of the size of the scroll bar, + // which is not available to us here. + // Instead, we pick a number that empirically matches Firefox's behavior. + return 160.0f / 3.0f; +} + +WebMouseWheelEvent WebMouseWheelEventBuilder::Build( + const GdkEventScroll* event) { + WebMouseWheelEvent result; + + result.type = WebInputEvent::MouseWheel; + result.button = WebMouseEvent::ButtonNone; + + result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time); + result.modifiers = GdkStateToWebEventModifiers(event->state); + result.x = static_cast<int>(event->x); + result.y = static_cast<int>(event->y); + result.windowX = result.x; + result.windowY = result.y; + result.globalX = static_cast<int>(event->x_root); + result.globalY = static_cast<int>(event->y_root); + + static const float scrollbarPixelsPerTick = ScrollbarPixelsPerTick(); + switch (event->direction) { + case GDK_SCROLL_UP: + result.deltaY = scrollbarPixelsPerTick; + result.wheelTicksY = 1; + break; + case GDK_SCROLL_DOWN: + result.deltaY = -scrollbarPixelsPerTick; + result.wheelTicksY = -1; + break; + case GDK_SCROLL_LEFT: + result.deltaX = scrollbarPixelsPerTick; + result.wheelTicksX = 1; + break; + case GDK_SCROLL_RIGHT: + result.deltaX = -scrollbarPixelsPerTick; + result.wheelTicksX = -1; + break; + } + + return result; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h new file mode 100644 index 00000000000..6fe16429b35 --- /dev/null +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk.h @@ -0,0 +1,44 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_ + +#include "content/common/content_export.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/keycodes/keyboard_codes.h" + +typedef struct _GdkEventButton GdkEventButton; +typedef struct _GdkEventMotion GdkEventMotion; +typedef struct _GdkEventCrossing GdkEventCrossing; +typedef struct _GdkEventScroll GdkEventScroll; +typedef struct _GdkEventKey GdkEventKey; + +namespace content { + +class CONTENT_EXPORT WebKeyboardEventBuilder { + public: + static WebKit::WebKeyboardEvent Build(const GdkEventKey* event); + static WebKit::WebKeyboardEvent Build(wchar_t character, + int state, + double time_secs); +}; + +class CONTENT_EXPORT WebMouseEventBuilder { + public: + static WebKit::WebMouseEvent Build(const GdkEventButton* event); + static WebKit::WebMouseEvent Build(const GdkEventMotion* event); + static WebKit::WebMouseEvent Build(const GdkEventCrossing* event); +}; + +class CONTENT_EXPORT WebMouseWheelEventBuilder { + public: + static float ScrollbarPixelsPerTick(); + static WebKit::WebMouseWheelEvent Build( + const GdkEventScroll* event); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H diff --git a/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc new file mode 100644 index 00000000000..9498c9eb1be --- /dev/null +++ b/chromium/content/browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc @@ -0,0 +1,171 @@ +// Copyright 2013 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/browser/renderer_host/input/web_input_event_builders_gtk.h" + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/keycodes/keyboard_code_conversion_gtk.h" + +using WebKit::WebInputEvent; +using WebKit::WebKeyboardEvent; +using WebKit::WebMouseEvent; +using content::WebMouseEventBuilder; +using content::WebKeyboardEventBuilder; + +namespace { + +TEST(WebMouseEventBuilderTest, DoubleClick) { + GdkEventButton first_click; + first_click.type = GDK_BUTTON_PRESS; + first_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1)); + first_click.x = first_click.y = first_click.x_root = first_click.y_root = 100; + first_click.state = 0; + first_click.time = 0; + first_click.button = 1; + + // Single click works. + WebMouseEvent first_click_events = + WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Make sure double click works. + GdkEventButton second_click = first_click; + second_click.time = first_click.time + 100; + WebMouseEvent second_click_events = + WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(2, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks with a long gap in between aren't counted as a double click. + second_click = first_click; + second_click.time = first_click.time + 1000; + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks far apart (horizontally) aren't counted as a double click. + second_click = first_click; + second_click.time = first_click.time + 1; + second_click.x = first_click.x + 100; + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks far apart (vertically) aren't counted as a double click. + second_click = first_click; + second_click.time = first_click.time + 1; + second_click.x = first_click.y + 100; + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); + + // Reset the click count. + first_click.time += 10000; + first_click_events = WebMouseEventBuilder::Build(&first_click); + EXPECT_EQ(1, first_click_events.clickCount); + + // Two clicks on different windows aren't a double click. + second_click = first_click; + second_click.time = first_click.time + 1; + second_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(2)); + second_click_events = WebMouseEventBuilder::Build(&second_click); + EXPECT_EQ(1, second_click_events.clickCount); +} + +TEST(WebMouseEventBuilderTest, MouseUpClickCount) { + GdkEventButton mouse_down; + memset(&mouse_down, 0, sizeof(mouse_down)); + mouse_down.type = GDK_BUTTON_PRESS; + mouse_down.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1)); + mouse_down.x = mouse_down.y = mouse_down.x_root = mouse_down.y_root = 100; + mouse_down.time = 0; + mouse_down.button = 1; + + // Properly set the last click time, so that the internal state won't be + // affected by previous tests. + WebMouseEventBuilder::Build(&mouse_down); + + mouse_down.time += 10000; + GdkEventButton mouse_up = mouse_down; + mouse_up.type = GDK_BUTTON_RELEASE; + WebMouseEvent mouse_down_event; + WebMouseEvent mouse_up_event; + + // Click for three times. + for (int i = 1; i < 4; ++i) { + mouse_down.time += 100; + mouse_down_event = WebMouseEventBuilder::Build(&mouse_down); + EXPECT_EQ(i, mouse_down_event.clickCount); + + mouse_up.time = mouse_down.time + 50; + mouse_up_event = WebMouseEventBuilder::Build(&mouse_up); + EXPECT_EQ(i, mouse_up_event.clickCount); + } + + // Reset the click count. + mouse_down.time += 10000; + mouse_down_event = WebMouseEventBuilder::Build(&mouse_down); + EXPECT_EQ(1, mouse_down_event.clickCount); + + // Moving the cursor for a significant distance will reset the click count to + // 0. + GdkEventMotion mouse_move; + memset(&mouse_move, 0, sizeof(mouse_move)); + mouse_move.type = GDK_MOTION_NOTIFY; + mouse_move.window = mouse_down.window; + mouse_move.time = mouse_down.time; + mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root = + mouse_down.x + 100; + WebMouseEventBuilder::Build(&mouse_move); + + mouse_up.time = mouse_down.time + 50; + mouse_up_event = WebMouseEventBuilder::Build(&mouse_up); + EXPECT_EQ(0, mouse_up_event.clickCount); + + // Reset the click count. + mouse_down.time += 10000; + mouse_down_event = WebMouseEventBuilder::Build(&mouse_down); + EXPECT_EQ(1, mouse_down_event.clickCount); + + // Moving the cursor with a significant delay will reset the click count to 0. + mouse_move.time = mouse_down.time + 1000; + mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root = + mouse_down.x; + WebMouseEventBuilder::Build(&mouse_move); + + mouse_up.time = mouse_move.time + 50; + mouse_up_event = WebMouseEventBuilder::Build(&mouse_up); + EXPECT_EQ(0, mouse_up_event.clickCount); +} + +TEST(WebKeyboardEventBuilderTest, NumPadConversion) { + // Construct a GDK input event for the numpad "5" key. + char five[] = "5"; + GdkEventKey gdk_event; + memset(&gdk_event, 0, sizeof(GdkEventKey)); + gdk_event.type = GDK_KEY_PRESS; + gdk_event.keyval = GDK_KP_5; + gdk_event.string = five; + + // Numpad flag should be set on the WebKeyboardEvent. + WebKeyboardEvent web_event = WebKeyboardEventBuilder::Build(&gdk_event); + EXPECT_TRUE(web_event.modifiers & WebInputEvent::IsKeyPad); +} + +} // anonymous namespace diff --git a/chromium/content/browser/renderer_host/input/web_input_event_util.h b/chromium/content/browser/renderer_host/input/web_input_event_util.h index 75870ebc7cc..1abaf8cf34e 100644 --- a/chromium/content/browser/renderer_host/input/web_input_event_util.h +++ b/chromium/content/browser/renderer_host/input/web_input_event_util.h @@ -6,7 +6,7 @@ #define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_UTIL_H_ #include "content/common/content_export.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace WebKit { class WebKeyboardEvent; diff --git a/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h b/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h index 34e4ee96165..e7dc1549cb9 100644 --- a/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h +++ b/chromium/content/browser/renderer_host/input/web_input_event_util_posix.h @@ -6,7 +6,7 @@ #define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_UTIL_POSIX_H_ #include "third_party/WebKit/public/web/WebInputEvent.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace content { diff --git a/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc b/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc index b4959567a31..50bb3ec8310 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_device_manager.cc @@ -34,7 +34,8 @@ AudioInputDeviceManager::AudioInputDeviceManager( StreamDeviceInfo fake_device(MEDIA_DEVICE_AUDIO_CAPTURE, media::AudioManagerBase::kDefaultDeviceName, media::AudioManagerBase::kDefaultDeviceId, - 44100, media::CHANNEL_LAYOUT_STEREO, false); + 44100, media::CHANNEL_LAYOUT_STEREO, + 0, false); fake_device.session_id = kFakeOpenSessionId; devices_.push_back(fake_device); } @@ -169,19 +170,37 @@ void AudioInputDeviceManager::OpenOnDeviceThread( DCHECK(IsOnDeviceThread()); StreamDeviceInfo out(info.device.type, info.device.name, info.device.id, - 0, 0, false); + 0, 0, 0, false); out.session_id = session_id; + + MediaStreamDevice::AudioDeviceParameters& input_params = out.device.input; + if (use_fake_device_) { // Don't need to query the hardware information if using fake device. - out.device.sample_rate = 44100; - out.device.channel_layout = media::CHANNEL_LAYOUT_STEREO; + input_params.sample_rate = 44100; + input_params.channel_layout = media::CHANNEL_LAYOUT_STEREO; } else { // Get the preferred sample rate and channel configuration for the // audio device. media::AudioParameters params = audio_manager_->GetInputStreamParameters(info.device.id); - out.device.sample_rate = params.sample_rate(); - out.device.channel_layout = params.channel_layout(); + input_params.sample_rate = params.sample_rate(); + input_params.channel_layout = params.channel_layout(); + input_params.frames_per_buffer = params.frames_per_buffer(); + + // Add preferred output device information if a matching output device + // exists. + out.device.matched_output_device_id = + audio_manager_->GetAssociatedOutputDeviceID(info.device.id); + if (!out.device.matched_output_device_id.empty()) { + params = audio_manager_->GetOutputStreamParameters( + out.device.matched_output_device_id); + MediaStreamDevice::AudioDeviceParameters& matched_output_params = + out.device.matched_output; + matched_output_params.sample_rate = params.sample_rate(); + matched_output_params.channel_layout = params.channel_layout(); + matched_output_params.frames_per_buffer = params.frames_per_buffer(); + } } // Return the |session_id| through the listener by posting a task on @@ -206,6 +225,7 @@ void AudioInputDeviceManager::OpenedOnIOThread(int session_id, DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_EQ(session_id, info.session_id); DCHECK(GetDevice(session_id) == devices_.end()); + devices_.push_back(info); if (listener_) diff --git a/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc index 03b31d2845e..25d8a1722e9 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc @@ -177,7 +177,8 @@ TEST_F(AudioInputDeviceManagerTest, OpenNotExistingDevice) { int sample_rate(0); int channel_config(0); StreamDeviceInfo dummy_device( - stream_type, device_name, device_id, sample_rate, channel_config, false); + stream_type, device_name, device_id, sample_rate, channel_config, 2048, + false); int session_id = manager_->Open(dummy_device); EXPECT_CALL(*audio_input_listener_, diff --git a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc index 4a2f731a81c..1c021095676 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc @@ -51,11 +51,12 @@ AudioInputRendererHost::AudioEntry::~AudioEntry() {} AudioInputRendererHost::AudioInputRendererHost( media::AudioManager* audio_manager, MediaStreamManager* media_stream_manager, - AudioMirroringManager* audio_mirroring_manager) + AudioMirroringManager* audio_mirroring_manager, + media::UserInputMonitor* user_input_monitor) : audio_manager_(audio_manager), media_stream_manager_(media_stream_manager), - audio_mirroring_manager_(audio_mirroring_manager) { -} + audio_mirroring_manager_(audio_mirroring_manager), + user_input_monitor_(user_input_monitor) {} AudioInputRendererHost::~AudioInputRendererHost() { DCHECK(audio_entries_.empty()); @@ -272,20 +273,23 @@ void AudioInputRendererHost::OnCreateStream( entry->controller = media::AudioInputController::CreateForStream( audio_manager_->GetMessageLoop(), this, - WebContentsAudioInputStream::Create( - device_id, audio_params, audio_manager_->GetWorkerLoop(), - audio_mirroring_manager_), - entry->writer.get()); + WebContentsAudioInputStream::Create(device_id, + audio_params, + audio_manager_->GetWorkerLoop(), + audio_mirroring_manager_), + entry->writer.get(), + user_input_monitor_); } else { // TODO(henrika): replace CreateLowLatency() with Create() as soon // as satish has ensured that Speech Input also uses the default low- // latency path. See crbug.com/112472 for details. - entry->controller = media::AudioInputController::CreateLowLatency( - audio_manager_, - this, - audio_params, - device_id, - entry->writer.get()); + entry->controller = + media::AudioInputController::CreateLowLatency(audio_manager_, + this, + audio_params, + device_id, + entry->writer.get(), + user_input_monitor_); } if (!entry->controller.get()) { diff --git a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h index d16ebfad86a..5df1fc6f2f0 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h +++ b/chromium/content/browser/renderer_host/media/audio_input_renderer_host.h @@ -44,6 +44,7 @@ namespace media { class AudioManager; class AudioParameters; +class UserInputMonitor; } namespace content { @@ -55,10 +56,11 @@ class CONTENT_EXPORT AudioInputRendererHost public media::AudioInputController::EventHandler { public: // Called from UI thread from the owner of this object. - AudioInputRendererHost( - media::AudioManager* audio_manager, - MediaStreamManager* media_stream_manager, - AudioMirroringManager* audio_mirroring_manager); + // |user_input_monitor| is used for typing detection and can be NULL. + AudioInputRendererHost(media::AudioManager* audio_manager, + MediaStreamManager* media_stream_manager, + AudioMirroringManager* audio_mirroring_manager, + media::UserInputMonitor* user_input_monitor); // BrowserMessageFilter implementation. virtual void OnChannelClosing() OVERRIDE; @@ -154,6 +156,9 @@ class CONTENT_EXPORT AudioInputRendererHost // A map of stream IDs to audio sources. AudioEntryMap audio_entries_; + // Raw pointer of the UserInputMonitor. + media::UserInputMonitor* user_input_monitor_; + DISALLOW_COPY_AND_ASSIGN(AudioInputRendererHost); }; diff --git a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc index 572abf3e1c6..369e0a87f20 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc +++ b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.cc @@ -29,14 +29,17 @@ void AudioInputSyncWriter::UpdateRecordedBytes(uint32 bytes) { socket_->Send(&bytes, sizeof(bytes)); } -uint32 AudioInputSyncWriter::Write( - const void* data, uint32 size, double volume) { +uint32 AudioInputSyncWriter::Write(const void* data, + uint32 size, + double volume, + bool key_pressed) { uint8* ptr = static_cast<uint8*>(shared_memory_->memory()); ptr += current_segment_id_ * shared_memory_segment_size_; media::AudioInputBuffer* buffer = reinterpret_cast<media::AudioInputBuffer*>(ptr); buffer->params.volume = volume; buffer->params.size = size; + buffer->params.key_pressed = key_pressed; memcpy(buffer->audio, data, size); if (++current_segment_id_ >= shared_memory_segment_count_) diff --git a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h index 4cfe9e3f396..d16911f20c3 100644 --- a/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h +++ b/chromium/content/browser/renderer_host/media/audio_input_sync_writer.h @@ -28,7 +28,10 @@ class AudioInputSyncWriter : public media::AudioInputController::SyncWriter { // media::AudioOutputController::SyncWriter implementation. virtual void UpdateRecordedBytes(uint32 bytes) OVERRIDE; - virtual uint32 Write(const void* data, uint32 size, double volume) OVERRIDE; + virtual uint32 Write(const void* data, + uint32 size, + double volume, + bool key_pressed) OVERRIDE; virtual void Close() OVERRIDE; bool Init(); diff --git a/chromium/content/browser/renderer_host/media/audio_renderer_host.cc b/chromium/content/browser/renderer_host/media/audio_renderer_host.cc index 53f2eb2ae90..c09dc6ce08b 100644 --- a/chromium/content/browser/renderer_host/media/audio_renderer_host.cc +++ b/chromium/content/browser/renderer_host/media/audio_renderer_host.cc @@ -36,6 +36,7 @@ class AudioRendererHost::AudioEntry int stream_id, int render_view_id, const media::AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, scoped_ptr<base::SharedMemory> shared_memory, scoped_ptr<media::AudioOutputController::SyncReader> reader); @@ -88,6 +89,7 @@ class AudioRendererHost::AudioEntry AudioRendererHost::AudioEntry::AudioEntry( AudioRendererHost* host, int stream_id, int render_view_id, const media::AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, scoped_ptr<base::SharedMemory> shared_memory, scoped_ptr<media::AudioOutputController::SyncReader> reader) @@ -95,7 +97,8 @@ AudioRendererHost::AudioEntry::AudioEntry( stream_id_(stream_id), render_view_id_(render_view_id), controller_(media::AudioOutputController::Create( - host->audio_manager_, this, params, input_device_id, reader.get())), + host->audio_manager_, this, params, output_device_id, + input_device_id, reader.get())), shared_memory_(shared_memory.Pass()), reader_(reader.Pass()) { DCHECK(controller_.get()); @@ -301,10 +304,16 @@ void AudioRendererHost::OnCreateStream( // When the |input_channels| is valid, clients are trying to create a unified // IO stream which opens an input device mapping to the |session_id|. - std::string input_device_id; + // Initialize the |output_device_id| to an empty string which indicates that + // the default device should be used. If a StreamDeviceInfo instance was found + // though, then we use the matched output device. + std::string input_device_id, output_device_id; + const StreamDeviceInfo* info = media_stream_manager_-> + audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); + if (info) + output_device_id = info->device.matched_output_device_id; + if (input_channels > 0) { - const StreamDeviceInfo* info = media_stream_manager_-> - audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); if (!info) { SendErrorMessage(stream_id); DLOG(WARNING) << "No permission has been granted to input stream with " @@ -346,16 +355,18 @@ void AudioRendererHost::OnCreateStream( media_observer->OnCreatingAudioStream(render_process_id_, render_view_id); scoped_ptr<AudioEntry> entry(new AudioEntry( - this, stream_id, render_view_id, params, input_device_id, - shared_memory.Pass(), + this, stream_id, render_view_id, params, output_device_id, + input_device_id, shared_memory.Pass(), reader.PassAs<media::AudioOutputController::SyncReader>())); if (mirroring_manager_) { mirroring_manager_->AddDiverter( render_process_id_, entry->render_view_id(), entry->controller()); } audio_entries_.insert(std::make_pair(stream_id, entry.release())); - if (media_internals_) - media_internals_->OnSetAudioStreamStatus(this, stream_id, "created"); + if (media_internals_) { + media_internals_->OnAudioStreamCreated( + this, stream_id, params, input_device_id); + } } void AudioRendererHost::OnPlayStream(int stream_id) { diff --git a/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc b/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc index 42fecc07a96..26c0963e5e8 100644 --- a/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc @@ -207,8 +207,6 @@ class AudioRendererHostTest : public testing::Test { } void Create(bool unified_stream) { - EXPECT_CALL(*observer_, - OnSetAudioStreamStatus(_, kStreamId, "created")); EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _)) .WillOnce(DoAll(Assign(&is_stream_active_, true), QuitMessageLoop(message_loop_.get()))); diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device.cc b/chromium/content/browser/renderer_host/media/desktop_capture_device.cc index 5a03e465533..16e69bf8ad0 100644 --- a/chromium/content/browser/renderer_host/media/desktop_capture_device.cc +++ b/chromium/content/browser/renderer_host/media/desktop_capture_device.cc @@ -23,7 +23,19 @@ namespace content { namespace { + const int kBytesPerPixel = 4; + +webrtc::DesktopRect ComputeLetterboxRect( + const webrtc::DesktopSize& max_size, + const webrtc::DesktopSize& source_size) { + gfx::Rect result = media::ComputeLetterboxRegion( + gfx::Rect(0, 0, max_size.width(), max_size.height()), + gfx::Size(source_size.width(), source_size.height())); + return webrtc::DesktopRect::MakeLTRB( + result.x(), result.y(), result.right(), result.bottom()); +} + } // namespace class DesktopCaptureDevice::Core @@ -34,8 +46,7 @@ class DesktopCaptureDevice::Core scoped_ptr<webrtc::DesktopCapturer> capturer); // Implementation of VideoCaptureDevice methods. - void Allocate(int width, int height, - int frame_rate, + void Allocate(const media::VideoCaptureCapability& capture_format, EventHandler* event_handler); void Start(); void Stop(); @@ -51,11 +62,16 @@ class DesktopCaptureDevice::Core // Helper methods that run on the |task_runner_|. Posted from the // corresponding public methods. - void DoAllocate(int width, int height, int frame_rate); + void DoAllocate(const media::VideoCaptureCapability& capture_format); void DoStart(); void DoStop(); void DoDeAllocate(); + // Chooses new output properties based on the supplied source size and the + // properties requested to Allocate(), and dispatches OnFrameInfo[Changed] + // notifications. + void RefreshCaptureFormat(const webrtc::DesktopSize& frame_size); + // Helper to schedule capture tasks. void ScheduleCaptureTimer(); @@ -78,24 +94,23 @@ class DesktopCaptureDevice::Core base::Lock event_handler_lock_; EventHandler* event_handler_; - // Requested size specified to Allocate(). - webrtc::DesktopSize requested_size_; - - // Frame rate specified to Allocate(). - int frame_rate_; + // Requested video capture format (width, height, frame rate, etc). + media::VideoCaptureCapability requested_format_; - // Empty until the first frame has been captured, and the output dimensions - // chosen based on the capture frame's size, and any caller-supplied - // size constraints. - webrtc::DesktopSize output_size_; + // Actual video capture format being generated. + media::VideoCaptureCapability capture_format_; - // Size of the most recently received frame. + // Size of frame most recently captured from the source. webrtc::DesktopSize previous_frame_size_; - // DesktopFrame into which captured frames are scaled, if the source size does - // not match |output_size_|. If the source and output have the same dimensions - // then this is NULL. - scoped_ptr<webrtc::DesktopFrame> scaled_frame_; + // DesktopFrame into which captured frames are down-scaled and/or letterboxed, + // depending upon the caller's requested capture capabilities. If frames can + // be returned to the caller directly then this is NULL. + scoped_ptr<webrtc::DesktopFrame> output_frame_; + + // Sub-rectangle of |output_frame_| into which the source will be scaled + // and/or letterboxed. + webrtc::DesktopRect output_rect_; // True between DoStart() and DoStop(). Can't just check |event_handler_| // because |event_handler_| is used on the caller thread. @@ -125,12 +140,12 @@ DesktopCaptureDevice::Core::Core( DesktopCaptureDevice::Core::~Core() { } -void DesktopCaptureDevice::Core::Allocate(int width, int height, - int frame_rate, - EventHandler* event_handler) { - DCHECK_GT(width, 0); - DCHECK_GT(height, 0); - DCHECK_GT(frame_rate, 0); +void DesktopCaptureDevice::Core::Allocate( + const media::VideoCaptureCapability& capture_format, + EventHandler* event_handler) { + DCHECK_GT(capture_format.width, 0); + DCHECK_GT(capture_format.height, 0); + DCHECK_GT(capture_format.frame_rate, 0); { base::AutoLock auto_lock(event_handler_lock_); @@ -139,7 +154,7 @@ void DesktopCaptureDevice::Core::Allocate(int width, int height, task_runner_->PostTask( FROM_HERE, - base::Bind(&Core::DoAllocate, this, width, height, frame_rate)); + base::Bind(&Core::DoAllocate, this, capture_format)); } void DesktopCaptureDevice::Core::Start() { @@ -180,95 +195,76 @@ void DesktopCaptureDevice::Core::OnCaptureCompleted( scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); - // If an |output_size_| hasn't yet been chosen then choose one, based upon - // the source frame size and the requested size supplied to Allocate(). - if (output_size_.is_empty()) { - // Treat the requested size as upper bounds on width & height. - // TODO(wez): Constraints should be passed from getUserMedia to Allocate. - output_size_.set( - std::min(frame->size().width(), requested_size_.width()), - std::min(frame->size().height(), requested_size_.height())); - - // Inform the EventHandler of the output dimensions, format and frame rate. - media::VideoCaptureCapability caps; - caps.width = output_size_.width(); - caps.height = output_size_.height(); - caps.frame_rate = frame_rate_; - caps.color = media::VideoCaptureCapability::kARGB; - caps.expected_capture_delay = - base::Time::kMillisecondsPerSecond / frame_rate_; - caps.interlaced = false; - - base::AutoLock auto_lock(event_handler_lock_); - if (event_handler_) - event_handler_->OnFrameInfo(caps); - } + // Handle initial frame size and size changes. + RefreshCaptureFormat(frame->size()); if (!started_) return; - size_t output_bytes = output_size_.width() * output_size_.height() * + webrtc::DesktopSize output_size(capture_format_.width, + capture_format_.height); + size_t output_bytes = output_size.width() * output_size.height() * webrtc::DesktopFrame::kBytesPerPixel; + const uint8_t* output_data = NULL; - if (frame->size().equals(output_size_)) { + if (frame->size().equals(output_size)) { // If the captured frame matches the output size, we can return the pixel // data directly, without scaling. - scaled_frame_.reset(); - - base::AutoLock auto_lock(event_handler_lock_); - if (event_handler_) { - event_handler_->OnIncomingCapturedFrame( - frame->data(), output_bytes, base::Time::Now(), 0, false, false); + output_data = frame->data(); + } else { + // Otherwise we need to down-scale and/or letterbox to the target format. + + // Allocate a buffer of the correct size to scale the frame into. + // |output_frame_| is cleared whenever |output_rect_| changes, so we don't + // need to worry about clearing out stale pixel data in letterboxed areas. + if (!output_frame_) { + output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); + memset(output_frame_->data(), 0, output_bytes); } - return; - } - - // If the output size differs from the frame size (e.g. the source has changed - // from its original dimensions, or the caller specified size constraints) - // then we need to scale the image. - if (!scaled_frame_) - scaled_frame_.reset(new webrtc::BasicDesktopFrame(output_size_)); - DCHECK(scaled_frame_->size().equals(output_size_)); - - // If the source frame size changed then clear |scaled_frame_|'s pixels. - if (!previous_frame_size_.equals(frame->size())) { - previous_frame_size_ = frame->size(); - memset(scaled_frame_->data(), 0, output_bytes); + DCHECK(output_frame_->size().equals(output_size)); + + // TODO(wez): Optimize this to scale only changed portions of the output, + // using ARGBScaleClip(). + uint8_t* output_rect_data = output_frame_->data() + + output_frame_->stride() * output_rect_.top() + + webrtc::DesktopFrame::kBytesPerPixel * output_rect_.left(); + libyuv::ARGBScale(frame->data(), frame->stride(), + frame->size().width(), frame->size().height(), + output_rect_data, output_frame_->stride(), + output_rect_.width(), output_rect_.height(), + libyuv::kFilterBilinear); + output_data = output_frame_->data(); } - // Determine the output size preserving aspect, and center in output buffer. - gfx::Rect scaled_rect = media::ComputeLetterboxRegion( - gfx::Rect(0, 0, output_size_.width(), output_size_.height()), - gfx::Size(frame->size().width(), frame->size().height())); - uint8* scaled_data = scaled_frame_->data() + - scaled_frame_->stride() * scaled_rect.y() + - webrtc::DesktopFrame::kBytesPerPixel * scaled_rect.x(); - - // TODO(wez): Optimize this to scale only changed portions of the output, - // using ARGBScaleClip(). - libyuv::ARGBScale(frame->data(), frame->stride(), - frame->size().width(), frame->size().height(), - scaled_data, scaled_frame_->stride(), - scaled_rect.width(), scaled_rect.height(), - libyuv::kFilterBilinear); - base::AutoLock auto_lock(event_handler_lock_); if (event_handler_) { - event_handler_->OnIncomingCapturedFrame( - scaled_frame_->data(), output_bytes, - base::Time::Now(), 0, false, false); + event_handler_->OnIncomingCapturedFrame(output_data, output_bytes, + base::Time::Now(), 0, false, false); } } -void DesktopCaptureDevice::Core::DoAllocate(int width, - int height, - int frame_rate) { +void DesktopCaptureDevice::Core::DoAllocate( + const media::VideoCaptureCapability& capture_format) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); DCHECK(desktop_capturer_); - requested_size_.set(width, height); - output_size_.set(0, 0); - frame_rate_ = frame_rate; + requested_format_ = capture_format; + + // Store requested frame rate and calculate expected delay. + capture_format_.frame_rate = requested_format_.frame_rate; + capture_format_.expected_capture_delay = + base::Time::kMillisecondsPerSecond / requested_format_.frame_rate; + + // Support dynamic changes in resolution only if requester also does. + if (requested_format_.frame_size_type == + media::VariableResolutionVideoCaptureDevice) { + capture_format_.frame_size_type = + media::VariableResolutionVideoCaptureDevice; + } + + // This capturer always outputs ARGB, non-interlaced. + capture_format_.color = media::PIXEL_FORMAT_ARGB; + capture_format_.interlaced = false; desktop_capturer_->Start(this); @@ -288,14 +284,63 @@ void DesktopCaptureDevice::Core::DoStart() { void DesktopCaptureDevice::Core::DoStop() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); started_ = false; - scaled_frame_.reset(); + output_frame_.reset(); + previous_frame_size_.set(0, 0); } void DesktopCaptureDevice::Core::DoDeAllocate() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); DoStop(); desktop_capturer_.reset(); - output_size_.set(0, 0); +} + +void DesktopCaptureDevice::Core::RefreshCaptureFormat( + const webrtc::DesktopSize& frame_size) { + if (previous_frame_size_.equals(frame_size)) + return; + + // Clear the output frame, if any, since it will either need resizing, or + // clearing of stale data in letterbox areas, anyway. + output_frame_.reset(); + + if (previous_frame_size_.is_empty() || + requested_format_.frame_size_type == + media::VariableResolutionVideoCaptureDevice) { + // If this is the first frame, or the receiver supports variable resolution + // then determine the output size by treating the requested width & height + // as maxima. + if (frame_size.width() > requested_format_.width || + frame_size.height() > requested_format_.height) { + output_rect_ = ComputeLetterboxRect( + webrtc::DesktopSize(requested_format_.width, + requested_format_.height), + frame_size); + output_rect_.Translate(-output_rect_.left(), -output_rect_.top()); + } else { + output_rect_ = webrtc::DesktopRect::MakeSize(frame_size); + } + capture_format_.width = output_rect_.width(); + capture_format_.height = output_rect_.height(); + + { + base::AutoLock auto_lock(event_handler_lock_); + if (event_handler_) { + if (previous_frame_size_.is_empty()) { + event_handler_->OnFrameInfo(capture_format_); + } else { + event_handler_->OnFrameInfoChanged(capture_format_); + } + } + } + } else { + // Otherwise the output frame size cannot change, so just scale and + // letterbox. + output_rect_ = ComputeLetterboxRect( + webrtc::DesktopSize(capture_format_.width, capture_format_.height), + frame_size); + } + + previous_frame_size_ = frame_size; } void DesktopCaptureDevice::Core::ScheduleCaptureTimer() { @@ -303,7 +348,7 @@ void DesktopCaptureDevice::Core::ScheduleCaptureTimer() { capture_task_posted_ = true; task_runner_->PostDelayedTask( FROM_HERE, base::Bind(&Core::OnCaptureTimer, this), - base::TimeDelta::FromSeconds(1) / frame_rate_); + base::TimeDelta::FromSeconds(1) / capture_format_.frame_rate); } void DesktopCaptureDevice::Core::OnCaptureTimer() { @@ -343,11 +388,10 @@ scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create( case DesktopMediaID::TYPE_SCREEN: { scoped_ptr<webrtc::DesktopCapturer> capturer; -#if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) && defined(USE_X11) +#if defined(OS_CHROMEOS) && defined(USE_X11) // ScreenCapturerX11 polls by default, due to poor driver support for // DAMAGE. ChromeOS' drivers [can be patched to] support DAMAGE properly, - // so use it. However ARM driver seems to not support this properly, so - // disable it for ARM. See http://crbug.com/230105 . + // so use it. capturer.reset(webrtc::ScreenCapturer::CreateWithXDamage(true)); #elif defined(OS_WIN) // ScreenCapturerWin disables Aero by default. We don't want it disabled @@ -394,11 +438,8 @@ DesktopCaptureDevice::~DesktopCaptureDevice() { void DesktopCaptureDevice::Allocate( const media::VideoCaptureCapability& capture_format, - EventHandler* observer) { - core_->Allocate(capture_format.width, - capture_format.height, - capture_format.frame_rate, - observer); + EventHandler* event_handler) { + core_->Allocate(capture_format, event_handler); } void DesktopCaptureDevice::Start() { diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device.h b/chromium/content/browser/renderer_host/media/desktop_capture_device.h index 84422d3c5c9..8f60c7de1b8 100644 --- a/chromium/content/browser/renderer_host/media/desktop_capture_device.h +++ b/chromium/content/browser/renderer_host/media/desktop_capture_device.h @@ -25,7 +25,7 @@ struct DesktopMediaID; // DesktopCaptureDevice implements VideoCaptureDevice for screens and windows. // It's essentially an adapter between webrtc::DesktopCapturer and // VideoCaptureDevice. -class CONTENT_EXPORT DesktopCaptureDevice : public media::VideoCaptureDevice { +class CONTENT_EXPORT DesktopCaptureDevice : public media::VideoCaptureDevice1 { public: // Creates capturer for the specified |source| and then creates // DesktopCaptureDevice for it. May return NULL in case of a failure (e.g. if diff --git a/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc b/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc index 0f14585ea90..2b6479e3cb2 100644 --- a/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc +++ b/chromium/content/browser/renderer_host/media/desktop_capture_device_unittest.cc @@ -17,7 +17,9 @@ #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" using ::testing::_; +using ::testing::AnyNumber; using ::testing::DoAll; +using ::testing::Expectation; using ::testing::InvokeWithoutArgs; using ::testing::SaveArg; @@ -25,6 +27,10 @@ namespace content { namespace { +MATCHER_P2(EqualsCaptureCapability, width, height, "") { + return arg.width == width && arg.height == height; +} + const int kTestFrameWidth1 = 100; const int kTestFrameHeight1 = 100; const int kTestFrameWidth2 = 200; @@ -38,6 +44,8 @@ class MockFrameObserver : public media::VideoCaptureDevice::EventHandler { MOCK_METHOD0(ReserveOutputBuffer, scoped_refptr<media::VideoFrame>()); MOCK_METHOD0(OnError, void()); MOCK_METHOD1(OnFrameInfo, void(const media::VideoCaptureCapability& info)); + MOCK_METHOD1(OnFrameInfoChanged, + void(const media::VideoCaptureCapability& info)); MOCK_METHOD6(OnIncomingCapturedFrame, void(const uint8* data, int length, base::Time timestamp, @@ -124,7 +132,7 @@ TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) { InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); media::VideoCaptureCapability capture_format( - 640, 480, kFrameRate, media::VideoCaptureCapability::kI420, 0, false, + 640, 480, kFrameRate, media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); capture_device.Allocate(capture_format, &frame_observer); capture_device.Start(); @@ -135,14 +143,15 @@ TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) { EXPECT_GT(caps.width, 0); EXPECT_GT(caps.height, 0); EXPECT_EQ(kFrameRate, caps.frame_rate); - EXPECT_EQ(media::VideoCaptureCapability::kARGB, caps.color); + EXPECT_EQ(media::PIXEL_FORMAT_ARGB, caps.color); EXPECT_FALSE(caps.interlaced); EXPECT_EQ(caps.width * caps.height * 4, frame_size); } -// Test that screen capturer can handle resolution change without crashing. -TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChange) { +// Test that screen capturer behaves correctly if the source frame size changes +// but the caller cannot cope with variable resolution output. +TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) { FakeScreenCapturer* mock_capturer = new FakeScreenCapturer(); DesktopCaptureDevice capture_device( @@ -154,11 +163,14 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChange) { int frame_size; MockFrameObserver frame_observer; - EXPECT_CALL(frame_observer, OnFrameInfo(_)) + Expectation frame_info_called = EXPECT_CALL(frame_observer, OnFrameInfo(_)) .WillOnce(SaveArg<0>(&caps)); + EXPECT_CALL(frame_observer, OnFrameInfoChanged(_)) + .Times(0); EXPECT_CALL(frame_observer, OnError()) .Times(0); EXPECT_CALL(frame_observer, OnIncomingCapturedFrame(_, _, _, _, _, _)) + .After(frame_info_called) .WillRepeatedly(DoAll( SaveArg<1>(&frame_size), InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); @@ -167,27 +179,93 @@ TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChange) { kTestFrameWidth1, kTestFrameHeight1, kFrameRate, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); + capture_device.Allocate(capture_format, &frame_observer); capture_device.Start(); - // Capture first frame. + + // Capture at least two frames, to ensure that the source frame size has + // changed while capturing. EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); done_event.Reset(); - // Capture second frame. EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + capture_device.Stop(); capture_device.DeAllocate(); EXPECT_EQ(kTestFrameWidth1, caps.width); EXPECT_EQ(kTestFrameHeight1, caps.height); EXPECT_EQ(kFrameRate, caps.frame_rate); - EXPECT_EQ(media::VideoCaptureCapability::kARGB, caps.color); + EXPECT_EQ(media::PIXEL_FORMAT_ARGB, caps.color); EXPECT_FALSE(caps.interlaced); EXPECT_EQ(caps.width * caps.height * 4, frame_size); } +// Test that screen capturer behaves correctly if the source frame size changes +// and the caller can cope with variable resolution output. +TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) { + FakeScreenCapturer* mock_capturer = new FakeScreenCapturer(); + + DesktopCaptureDevice capture_device( + worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()), + scoped_ptr<webrtc::DesktopCapturer>(mock_capturer)); + + media::VideoCaptureCapability caps; + base::WaitableEvent done_event(false, false); + + MockFrameObserver frame_observer; + Expectation frame_info_called = EXPECT_CALL(frame_observer, OnFrameInfo(_)) + .WillOnce(SaveArg<0>(&caps)); + Expectation first_info_changed = EXPECT_CALL(frame_observer, + OnFrameInfoChanged(EqualsCaptureCapability(kTestFrameWidth2, + kTestFrameHeight2))) + .After(frame_info_called); + Expectation second_info_changed = EXPECT_CALL(frame_observer, + OnFrameInfoChanged(EqualsCaptureCapability(kTestFrameWidth1, + kTestFrameHeight1))) + .After(first_info_changed); + EXPECT_CALL(frame_observer, OnFrameInfoChanged(_)) + .Times(AnyNumber()) + .After(second_info_changed); + EXPECT_CALL(frame_observer, OnError()) + .Times(0); + EXPECT_CALL(frame_observer, OnIncomingCapturedFrame(_, _, _, _, _, _)) + .After(frame_info_called) + .WillRepeatedly( + InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)); + + media::VideoCaptureCapability capture_format( + kTestFrameWidth2, + kTestFrameHeight2, + kFrameRate, + media::PIXEL_FORMAT_I420, + 0, + false, + media::VariableResolutionVideoCaptureDevice); + + capture_device.Allocate(capture_format, &frame_observer); + capture_device.Start(); + + // Capture at least three frames, to ensure that the source frame size has + // changed at least twice while capturing. + EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + done_event.Reset(); + EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + done_event.Reset(); + EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); + + capture_device.Stop(); + capture_device.DeAllocate(); + + EXPECT_EQ(kTestFrameWidth1, caps.width); + EXPECT_EQ(kTestFrameHeight1, caps.height); + EXPECT_EQ(kFrameRate, caps.frame_rate); + EXPECT_EQ(media::PIXEL_FORMAT_ARGB, caps.color); + EXPECT_FALSE(caps.interlaced); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter.cc b/chromium/content/browser/renderer_host/media/device_request_message_filter.cc index 770e800d402..96a5de2e194 100644 --- a/chromium/content/browser/renderer_host/media/device_request_message_filter.cc +++ b/chromium/content/browser/renderer_host/media/device_request_message_filter.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - #include "content/browser/renderer_host/media/device_request_message_filter.h" #include "base/strings/string_number_conversions.h" @@ -193,7 +192,13 @@ bool DeviceRequestMessageFilter::DoesRawIdMatchGuid( bool result = hmac.Init(security_origin.spec()); DCHECK(result); std::vector<uint8> converted_guid; - base::HexStringToBytes(device_guid, &converted_guid); + // |device_guid| is not guaranteed to be a hex string, so check for success. + bool success = base::HexStringToBytes(device_guid, &converted_guid); + + if (!success) + return false; + + DCHECK_GT(converted_guid.size(), 0u); return hmac.Verify( raw_device_id, base::StringPiece(reinterpret_cast<const char*>(&converted_guid[0]), diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter.h b/chromium/content/browser/renderer_host/media/device_request_message_filter.h index 048e2157871..b684954384c 100644 --- a/chromium/content/browser/renderer_host/media/device_request_message_filter.h +++ b/chromium/content/browser/renderer_host/media/device_request_message_filter.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_CENTER_HOST_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_CENTER_HOST_H_ +#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_DEVICE_REQUEST_MESSAGE_FILTER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_DEVICE_REQUEST_MESSAGE_FILTER_H_ #include <map> #include <string> @@ -75,4 +75,4 @@ class CONTENT_EXPORT DeviceRequestMessageFilter : public BrowserMessageFilter, } // namespace content -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_CENTER_HOST_H_ +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_DEVICE_REQUEST_MESSAGE_FILTER_H_ diff --git a/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc b/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc index 04a7bb69eea..0daad820084 100644 --- a/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc +++ b/chromium/content/browser/renderer_host/media/device_request_message_filter_unittest.cc @@ -301,4 +301,20 @@ TEST_F(DeviceRequestMessageFilterTest, TestGetSources_AllowMicAllowCamera) { EXPECT_TRUE(AreLabelsPresent(MEDIA_DEVICE_VIDEO_CAPTURE)); } +TEST_F(DeviceRequestMessageFilterTest, TestRawIdMatchGuid_EmptyGuid) { + GURL origin("https://test.com"); + const std::string device_guid = ""; + const std::string raw_device_id = "device"; + EXPECT_FALSE(DeviceRequestMessageFilter::DoesRawIdMatchGuid( + origin, device_guid, raw_device_id)); +} + +TEST_F(DeviceRequestMessageFilterTest, TestRawIdMatchGuid_NonHexGuid) { + GURL origin("https://test.com"); + const std::string device_guid = "garbage"; + const std::string raw_device_id = "device"; + EXPECT_FALSE(DeviceRequestMessageFilter::DoesRawIdMatchGuid( + origin, device_guid, raw_device_id)); +} + }; // namespace content diff --git a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc index 05804266035..2b7d6836326 100644 --- a/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc @@ -274,16 +274,6 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateThreeStreams) { EXPECT_EQ(host_->NumberOfStreams(), 0u); } -TEST_F(MediaStreamDispatcherHostTest, FailOpenVideoDevice) { - StreamOptions options(MEDIA_NO_SERVICE, MEDIA_DEVICE_VIDEO_CAPTURE); - - media::FakeVideoCaptureDevice::SetFailNextCreate(); - SetupFakeUI(false); - EXPECT_CALL(*host_.get(), - OnStreamGenerationFailed(kRenderId, kPageRequestId)); - GenerateStreamAndWaitForResult(kPageRequestId, options); -} - TEST_F(MediaStreamDispatcherHostTest, CancelPendingStreamsOnChannelClosing) { StreamOptions options(MEDIA_NO_SERVICE, MEDIA_DEVICE_VIDEO_CAPTURE); diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager.cc b/chromium/content/browser/renderer_host/media/media_stream_manager.cc index 449331198bd..4c234b66ab1 100644 --- a/chromium/content/browser/renderer_host/media/media_stream_manager.cc +++ b/chromium/content/browser/renderer_host/media/media_stream_manager.cc @@ -740,8 +740,8 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, audio_input_device_manager_->GetOpenedDeviceInfoById( device_it->session_id); DCHECK_EQ(info->device.id, device_it->device.id); - device_it->device.sample_rate = info->device.sample_rate; - device_it->device.channel_layout = info->device.channel_layout; + device_it->device.input = info->device.input; + device_it->device.matched_output = info->device.matched_output; } audio_devices.push_back(*device_it); } else if (IsVideoMediaType(device_it->device.type)) { @@ -949,8 +949,8 @@ void MediaStreamManager::HandleAccessRequestResponse( if (sample_rate <= 0 || sample_rate > 96000) sample_rate = 44100; - device_info.device.sample_rate = sample_rate; - device_info.device.channel_layout = media::CHANNEL_LAYOUT_STEREO; + device_info.device.input.sample_rate = sample_rate; + device_info.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO; } } diff --git a/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc index 91413710020..a67237a1ed9 100644 --- a/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc +++ b/chromium/content/browser/renderer_host/media/media_stream_manager_unittest.cc @@ -50,6 +50,7 @@ class MockAudioManager : public AudioManagerPlatform { virtual void GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) OVERRIDE { + DCHECK(device_names->empty()); if (HasAudioInputDevices()) { AudioManagerBase::GetAudioInputDeviceNames(device_names); } else { diff --git a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc b/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc index 8ecbabcd47d..53f5a769313 100644 --- a/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc +++ b/chromium/content/browser/renderer_host/media/midi_dispatcher_host.cc @@ -5,6 +5,7 @@ #include "content/browser/renderer_host/media/midi_dispatcher_host.h" #include "base/bind.h" +#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/common/media/midi_messages.h" #include "content/public/browser/browser_context.h" @@ -57,6 +58,8 @@ void MIDIDispatcherHost::OnRequestSysExPermission(int render_view_id, void MIDIDispatcherHost::WasSysExPermissionGranted(int render_view_id, int client_id, bool success) { + ChildProcessSecurityPolicyImpl::GetInstance()->GrantSendMIDISysExMessage( + render_process_id_); Send(new MIDIMsg_SysExPermissionApproved(render_view_id, client_id, success)); } diff --git a/chromium/content/browser/renderer_host/media/midi_host.cc b/chromium/content/browser/renderer_host/media/midi_host.cc index 6ed473afeff..0467404bda3 100644 --- a/chromium/content/browser/renderer_host/media/midi_host.cc +++ b/chromium/content/browser/renderer_host/media/midi_host.cc @@ -9,10 +9,12 @@ #include "base/debug/trace_event.h" #include "base/process/process.h" #include "content/browser/browser_main_loop.h" +#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/media/media_internals.h" #include "content/common/media/midi_messages.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/media_observer.h" +#include "content/public/browser/user_metrics.h" #include "media/midi/midi_manager.h" using media::MIDIManager; @@ -32,8 +34,9 @@ static const uint8 kSysExMessage = 0xf0; namespace content { -MIDIHost::MIDIHost(media::MIDIManager* midi_manager) - : midi_manager_(midi_manager), +MIDIHost::MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager) + : renderer_process_id_(renderer_process_id), + midi_manager_(midi_manager), sent_bytes_in_flight_(0), bytes_sent_since_last_acknowledgement_(0) { } @@ -88,12 +91,15 @@ void MIDIHost::OnStartSession(int client_id) { output_ports)); } -void MIDIHost::OnSendData(int port, +void MIDIHost::OnSendData(uint32 port, const std::vector<uint8>& data, double timestamp) { if (!midi_manager_) return; + if (data.empty()) + return; + base::AutoLock auto_lock(in_flight_lock_); // Sanity check that we won't send too much. @@ -102,47 +108,48 @@ void MIDIHost::OnSendData(int port, data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes) return; - // For now disallow all System Exclusive messages even if we - // have permission. - // TODO(toyoshim): allow System Exclusive if browser has granted - // this client access. We'll likely need to pass a GURL - // here to compare against our permissions. - if (data.size() > 0 && data[0] >= kSysExMessage) + if (data[0] >= kSysExMessage) { + // Blink running in a renderer checks permission to raise a SecurityError in + // JavaScript. The actual permission check for security perposes happens + // here in the browser process. + if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMIDISysExMessage( + renderer_process_id_)) { + RecordAction(UserMetricsAction("BadMessageTerminate_MIDI")); + BadMessageReceived(); return; + } + } -#if defined(OS_ANDROID) - // TODO(toyoshim): figure out why data() method does not compile on Android. - NOTIMPLEMENTED(); -#else midi_manager_->DispatchSendMIDIData( this, port, - data.data(), - data.size(), + data, timestamp); -#endif sent_bytes_in_flight_ += data.size(); } void MIDIHost::ReceiveMIDIData( - int port_index, + uint32 port, const uint8* data, size_t length, double timestamp) { TRACE_EVENT0("midi", "MIDIHost::ReceiveMIDIData"); - // For now disallow all System Exclusive messages even if we - // have permission. - // TODO(toyoshim): allow System Exclusive if browser has granted - // this client access. We'll likely need to pass a GURL - // here to compare against our permissions. - if (length > 0 && data[0] >= kSysExMessage) + // Check a process security policy to receive a system exclusive message. + if (length > 0 && data[0] >= kSysExMessage) { + if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMIDISysExMessage( + renderer_process_id_)) { + // MIDI devices may send a system exclusive messages even if the renderer + // doesn't have a permission to receive it. Don't kill the renderer as + // OnSendData() does. return; + } + } // Send to the renderer. std::vector<uint8> v(data, data + length); - Send(new MIDIMsg_DataReceived(port_index, v, timestamp)); + Send(new MIDIMsg_DataReceived(port, v, timestamp)); } void MIDIHost::AccumulateMIDIBytesSent(size_t n) { diff --git a/chromium/content/browser/renderer_host/media/midi_host.h b/chromium/content/browser/renderer_host/media/midi_host.h index f6b2813264e..944325d99a0 100644 --- a/chromium/content/browser/renderer_host/media/midi_host.h +++ b/chromium/content/browser/renderer_host/media/midi_host.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_ +#include <vector> + #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "content/common/content_export.h" @@ -23,7 +25,7 @@ class CONTENT_EXPORT MIDIHost public media::MIDIManagerClient { public: // Called from UI thread from the owner of this object. - MIDIHost(media::MIDIManager* midi_manager); + MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager); // BrowserMessageFilter implementation. virtual void OnChannelClosing() OVERRIDE; @@ -33,7 +35,7 @@ class CONTENT_EXPORT MIDIHost // MIDIManagerClient implementation. virtual void ReceiveMIDIData( - int port_index, + uint32 port, const uint8* data, size_t length, double timestamp) OVERRIDE; @@ -43,7 +45,7 @@ class CONTENT_EXPORT MIDIHost void OnStartSession(int client_id); // Data to be sent to a MIDI output port. - void OnSendData(int port, + void OnSendData(uint32 port, const std::vector<uint8>& data, double timestamp); @@ -53,6 +55,8 @@ class CONTENT_EXPORT MIDIHost virtual ~MIDIHost(); + int renderer_process_id_; + // |midi_manager_| talks to the platform-specific MIDI APIs. // It can be NULL if the platform (or our current implementation) // does not support MIDI. If not supported then a call to diff --git a/chromium/content/browser/renderer_host/media/mock_media_observer.h b/chromium/content/browser/renderer_host/media/mock_media_observer.h index fc3734744ea..5f7fb847b3c 100644 --- a/chromium/content/browser/renderer_host/media/mock_media_observer.h +++ b/chromium/content/browser/renderer_host/media/mock_media_observer.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "content/browser/media/media_internals.h" #include "content/public/browser/media_observer.h" +#include "media/audio/audio_parameters.h" #include "media/base/media_log_event.h" #include "testing/gmock/include/gmock/gmock.h" @@ -47,6 +48,10 @@ class MockMediaInternals : public MediaInternals { void(void* host, int stream_id)); MOCK_METHOD3(OnSetAudioStreamPlaying, void(void* host, int stream_id, bool playing)); + MOCK_METHOD4(OnAudioStreamCreated, + void(void* host, int stream_id, + const media::AudioParameters& params, + const std::string& input_device_id)); MOCK_METHOD3(OnSetAudioStreamStatus, void(void* host, int stream_id, const std::string& status)); MOCK_METHOD3(OnSetAudioStreamVolume, diff --git a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc index 207f86a99f3..3e30834feb2 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool.cc @@ -128,8 +128,12 @@ int VideoCaptureBufferPool::RecognizeReservedBuffer( scoped_refptr<media::VideoFrame> VideoCaptureBufferPool::ReserveI420VideoFrame( const gfx::Size& size, int rotation) { - if (static_cast<size_t>(size.GetArea() * 3 / 2) != GetMemorySize()) + if (GetMemorySize() != + media::VideoFrame::AllocationSize(media::VideoFrame::I420, size)) { + DCHECK_EQ(GetMemorySize(), + media::VideoFrame::AllocationSize(media::VideoFrame::I420, size)); return NULL; + } base::AutoLock lock(lock_); @@ -151,6 +155,7 @@ scoped_refptr<media::VideoFrame> VideoCaptureBufferPool::ReserveI420VideoFrame( gfx::Rect(size), size, static_cast<uint8*>(buffer->shared_memory.memory()), + GetMemorySize(), buffer->shared_memory.handle(), base::TimeDelta(), disposal_handler); diff --git a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc index 67a8be4c761..30509a34c75 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc @@ -21,8 +21,8 @@ TEST(VideoCaptureBufferPoolTest, BufferPool) { scoped_refptr<media::VideoFrame> non_pool_frame = media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta()); - scoped_refptr<VideoCaptureBufferPool> pool = - new VideoCaptureBufferPool(size.GetArea() * 3 / 2, 3); + scoped_refptr<VideoCaptureBufferPool> pool = new VideoCaptureBufferPool( + media::VideoFrame::AllocationSize(media::VideoFrame::I420, size), 3); ASSERT_EQ(460800u, pool->GetMemorySize()); ASSERT_TRUE(pool->Allocate()); diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller.cc b/chromium/content/browser/renderer_host/media/video_capture_controller.cc index bac43289a21..687a21a4996 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_controller.cc @@ -7,6 +7,7 @@ #include <set> #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "content/browser/renderer_host/media/media_stream_manager.h" @@ -22,6 +23,7 @@ namespace { +#if defined(OS_IOS) || defined(OS_ANDROID) // TODO(wjia): Support stride. void RotatePackedYV12Frame( const uint8* src, @@ -43,6 +45,7 @@ void RotatePackedYV12Frame( media::RotatePlaneByPixels( src, dest_vplane, width/2, height/2, rotation, flip_vert, flip_horiz); } +#endif // #if defined(OS_IOS) || defined(OS_ANDROID) } // namespace @@ -77,31 +80,109 @@ struct VideoCaptureController::ControllerClient { // Buffers used by this client. std::set<int> buffers; - // State of capture session, controlled by VideoCaptureManager directly. + // State of capture session, controlled by VideoCaptureManager directly. This + // transitions to true as soon as StopSession() occurs, at which point the + // client is sent an OnEnded() event. However, because the client retains a + // VideoCaptureController* pointer, its ControllerClient entry lives on until + // it unregisters itself via RemoveClient(), which may happen asynchronously. + // + // TODO(nick): If we changed the semantics of VideoCaptureHost so that + // OnEnded() events were processed synchronously (with the RemoveClient() done + // implicitly), we could avoid tracking this state here in the Controller, and + // simplify the code in both places. bool session_closed; }; -VideoCaptureController::VideoCaptureController( - VideoCaptureManager* video_capture_manager) - : chopped_width_(0), - chopped_height_(0), - frame_info_available_(false), - video_capture_manager_(video_capture_manager), - device_in_use_(false), - state_(VIDEO_CAPTURE_STATE_STOPPED) { +// Receives events from the VideoCaptureDevice and posts them to a +// VideoCaptureController on the IO thread. An instance of this class may safely +// outlive its target VideoCaptureController. +// +// Methods of this class may be called from any thread, and in practice will +// often be called on some auxiliary thread depending on the platform and the +// device type; including, for example, the DirectShow thread on Windows, the +// v4l2_thread on Linux, and the UI thread for tab capture. +class VideoCaptureController::VideoCaptureDeviceClient + : public media::VideoCaptureDevice::EventHandler { + public: + explicit VideoCaptureDeviceClient( + const base::WeakPtr<VideoCaptureController>& controller); + virtual ~VideoCaptureDeviceClient(); + + // VideoCaptureDevice::EventHandler implementation. + virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE; + virtual void OnIncomingCapturedFrame(const uint8* data, + int length, + base::Time timestamp, + int rotation, + bool flip_vert, + bool flip_horiz) OVERRIDE; + virtual void OnIncomingCapturedVideoFrame( + const scoped_refptr<media::VideoFrame>& frame, + base::Time timestamp) OVERRIDE; + virtual void OnError() OVERRIDE; + virtual void OnFrameInfo( + const media::VideoCaptureCapability& info) OVERRIDE; + virtual void OnFrameInfoChanged( + const media::VideoCaptureCapability& info) OVERRIDE; + + private: + // The controller to which we post events. + const base::WeakPtr<VideoCaptureController> controller_; + + // The pool of shared-memory buffers used for capturing. + scoped_refptr<VideoCaptureBufferPool> buffer_pool_; + + // Chopped pixels in width/height in case video capture device has odd + // numbers for width/height. + int chopped_width_; + int chopped_height_; + + // Tracks the current frame format. + media::VideoCaptureCapability frame_info_; + + // For NV21 we have to do color conversion into the intermediate buffer and + // from there the rotations. This variable won't be needed after + // http://crbug.com/292400 +#if defined(OS_IOS) || defined(OS_ANDROID) + scoped_ptr<uint8[]> i420_intermediate_buffer_; +#endif // #if defined(OS_IOS) || defined(OS_ANDROID) +}; + +VideoCaptureController::VideoCaptureController() + : state_(VIDEO_CAPTURE_STATE_STARTED), + weak_ptr_factory_(this) { memset(¤t_params_, 0, sizeof(current_params_)); } -void VideoCaptureController::StartCapture( +VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient( + const base::WeakPtr<VideoCaptureController>& controller) + : controller_(controller), + chopped_width_(0), + chopped_height_(0) {} + +VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} + +base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +scoped_ptr<media::VideoCaptureDevice::EventHandler> +VideoCaptureController::NewDeviceClient() { + scoped_ptr<media::VideoCaptureDevice::EventHandler> result( + new VideoCaptureDeviceClient(this->GetWeakPtr())); + return result.Pass(); +} + +void VideoCaptureController::AddClient( const VideoCaptureControllerID& id, VideoCaptureControllerEventHandler* event_handler, base::ProcessHandle render_process, const media::VideoCaptureParams& params) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DVLOG(1) << "VideoCaptureController::StartCapture, id " << id.device_id + DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id << ", (" << params.width << ", " << params.height - << ", " << params.frame_per_second + << ", " << params.frame_rate << ", " << params.session_id << ")"; @@ -111,71 +192,32 @@ void VideoCaptureController::StartCapture( return; } - // Do nothing if this client has called StartCapture before. - if (FindClient(id, event_handler, controller_clients_) || - FindClient(id, event_handler, pending_clients_)) + // Do nothing if this client has called AddClient before. + if (FindClient(id, event_handler, controller_clients_)) return; ControllerClient* client = new ControllerClient(id, event_handler, render_process, params); - // In case capture has been started, need to check different conditions. + // If we already have gotten frame_info from the device, repeat it to the new + // client. if (state_ == VIDEO_CAPTURE_STATE_STARTED) { - // TODO(wjia): Temporarily disable restarting till client supports resampling. -#if 0 - // This client has higher resolution than what is currently requested. - // Need restart capturing. - if (params.width > current_params_.width || - params.height > current_params_.height) { - video_capture_manager_->Stop(current_params_.session_id, - base::Bind(&VideoCaptureController::OnDeviceStopped, this)); - frame_info_available_ = false; - state_ = VIDEO_CAPTURE_STATE_STOPPING; - pending_clients_.push_back(client); - return; - } -#endif - - // This client's resolution is no larger than what's currently requested. - // When frame_info has been returned by device, send them to client. - if (frame_info_available_) { + if (frame_info_.IsValid()) { SendFrameInfoAndBuffers(client); } controller_clients_.push_back(client); return; } - - // In case the device is in the middle of stopping, put the client in - // pending queue. - if (state_ == VIDEO_CAPTURE_STATE_STOPPING) { - pending_clients_.push_back(client); - return; - } - - // Fresh start. - controller_clients_.push_back(client); - current_params_ = params; - // Order the manager to start the actual capture. - video_capture_manager_->Start(params, this); - state_ = VIDEO_CAPTURE_STATE_STARTED; - device_in_use_ = true; } -void VideoCaptureController::StopCapture( +int VideoCaptureController::RemoveClient( const VideoCaptureControllerID& id, VideoCaptureControllerEventHandler* event_handler) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DVLOG(1) << "VideoCaptureController::StopCapture, id " << id.device_id; + DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id.device_id; - ControllerClient* client = FindClient(id, event_handler, pending_clients_); - // If the client is still in pending queue, just remove it. - if (client) { - pending_clients_.remove(client); - return; - } - - client = FindClient(id, event_handler, controller_clients_); + ControllerClient* client = FindClient(id, event_handler, controller_clients_); if (!client) - return; + return kInvalidMediaCaptureSessionId; // Take back all buffers held by the |client|. if (buffer_pool_.get()) { @@ -189,28 +231,17 @@ void VideoCaptureController::StopCapture( client->buffers.clear(); int session_id = client->parameters.session_id; - delete client; controller_clients_.remove(client); + delete client; - // No more clients. Stop device. - if (controller_clients_.empty() && - (state_ == VIDEO_CAPTURE_STATE_STARTED || - state_ == VIDEO_CAPTURE_STATE_ERROR)) { - video_capture_manager_->Stop(session_id, - base::Bind(&VideoCaptureController::OnDeviceStopped, this)); - frame_info_available_ = false; - state_ = VIDEO_CAPTURE_STATE_STOPPING; - } + return session_id; } -void VideoCaptureController::StopSession( - int session_id) { +void VideoCaptureController::StopSession(int session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id; - ControllerClient* client = FindClient(session_id, pending_clients_); - if (!client) - client = FindClient(session_id, controller_clients_); + ControllerClient* client = FindClient(session_id, controller_clients_); if (client) { client->session_closed = true; @@ -224,59 +255,174 @@ void VideoCaptureController::ReturnBuffer( int buffer_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ControllerClient* client = FindClient(id, event_handler, - controller_clients_); + ControllerClient* client = FindClient(id, event_handler, controller_clients_); // If this buffer is not held by this client, or this client doesn't exist // in controller, do nothing. if (!client || - client->buffers.find(buffer_id) == client->buffers.end()) + client->buffers.find(buffer_id) == client->buffers.end()) { + NOTREACHED(); return; + } client->buffers.erase(buffer_id); buffer_pool_->RelinquishConsumerHold(buffer_id, 1); - - // When all buffers have been returned by clients and device has been - // called to stop, check if restart is needed. This could happen when - // capture needs to be restarted due to resolution change. - if (!buffer_pool_->IsAnyBufferHeldForConsumers() && - state_ == VIDEO_CAPTURE_STATE_STOPPING) { - PostStopping(); - } } -scoped_refptr<media::VideoFrame> VideoCaptureController::ReserveOutputBuffer() { - base::AutoLock lock(buffer_pool_lock_); - if (!buffer_pool_.get()) - return NULL; +scoped_refptr<media::VideoFrame> +VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer() { return buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, frame_info_.height), 0); } -// Implements VideoCaptureDevice::EventHandler. -// OnIncomingCapturedFrame is called the thread running the capture device. -// I.e.- DirectShow thread on windows and v4l2_thread on Linux. -void VideoCaptureController::OnIncomingCapturedFrame( +#if !defined(OS_IOS) && !defined(OS_ANDROID) +void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( const uint8* data, int length, base::Time timestamp, int rotation, bool flip_vert, bool flip_horiz) { - DCHECK(frame_info_.color == media::VideoCaptureCapability::kI420 || - frame_info_.color == media::VideoCaptureCapability::kYV12 || - (rotation == 0 && !flip_vert && !flip_horiz)); + TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); - scoped_refptr<media::VideoFrame> dst; - { - base::AutoLock lock(buffer_pool_lock_); - if (!buffer_pool_.get()) - return; - dst = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, - frame_info_.height), - rotation); + if (!buffer_pool_.get()) + return; + scoped_refptr<media::VideoFrame> dst = buffer_pool_->ReserveI420VideoFrame( + gfx::Size(frame_info_.width, frame_info_.height), rotation); + + if (!dst.get()) + return; + + uint8* yplane = dst->data(media::VideoFrame::kYPlane); + uint8* uplane = dst->data(media::VideoFrame::kUPlane); + uint8* vplane = dst->data(media::VideoFrame::kVPlane); + int yplane_stride = frame_info_.width; + int uv_plane_stride = (frame_info_.width + 1) / 2; + int crop_x = 0; + int crop_y = 0; + libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY; + // Assuming rotation happens first and flips next, we can consolidate both + // vertical and horizontal flips together with rotation into two variables: + // new_rotation = (rotation + 180 * vertical_flip) modulo 360 + // new_vertical_flip = horizontal_flip XOR vertical_flip + int new_rotation_angle = (rotation + 180 * flip_vert) % 360; + libyuv::RotationMode rotation_mode = libyuv::kRotate0; + if (new_rotation_angle == 90) + rotation_mode = libyuv::kRotate90; + else if (new_rotation_angle == 180) + rotation_mode = libyuv::kRotate180; + else if (new_rotation_angle == 270) + rotation_mode = libyuv::kRotate270; + + switch (frame_info_.color) { + case media::PIXEL_FORMAT_UNKNOWN: // Color format not set. + break; + case media::PIXEL_FORMAT_I420: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_I420; + break; + case media::PIXEL_FORMAT_YV12: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_YV12; + break; + case media::PIXEL_FORMAT_NV21: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_NV12; + break; + case media::PIXEL_FORMAT_YUY2: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_YUY2; + break; + case media::PIXEL_FORMAT_UYVY: + DCHECK(!chopped_width_ && !chopped_height_); + origin_colorspace = libyuv::FOURCC_UYVY; + break; + case media::PIXEL_FORMAT_RGB24: + origin_colorspace = libyuv::FOURCC_RAW; + break; + case media::PIXEL_FORMAT_ARGB: + origin_colorspace = libyuv::FOURCC_ARGB; + break; + case media::PIXEL_FORMAT_MJPEG: + origin_colorspace = libyuv::FOURCC_MJPG; + break; + default: + NOTREACHED(); + } + + int need_convert_rgb24_on_win = false; +#if defined(OS_WIN) + // kRGB24 on Windows start at the bottom line and has a negative stride. This + // is not supported by libyuv, so the media API is used instead. + if (frame_info_.color == media::PIXEL_FORMAT_RGB24) { + // Rotation and flipping is not supported in kRGB24 and OS_WIN case. + DCHECK(!rotation && !flip_vert && !flip_horiz); + need_convert_rgb24_on_win = true; } +#endif + if (need_convert_rgb24_on_win) { + int rgb_stride = -3 * (frame_info_.width + chopped_width_); + const uint8* rgb_src = + data + 3 * (frame_info_.width + chopped_width_) * + (frame_info_.height - 1 + chopped_height_); + media::ConvertRGB24ToYUV(rgb_src, + yplane, + uplane, + vplane, + frame_info_.width, + frame_info_.height, + rgb_stride, + yplane_stride, + uv_plane_stride); + } else { + libyuv::ConvertToI420( + data, + length, + yplane, + yplane_stride, + uplane, + uv_plane_stride, + vplane, + uv_plane_stride, + crop_x, + crop_y, + frame_info_.width + chopped_width_, + frame_info_.height * (flip_vert ^ flip_horiz ? -1 : 1), + frame_info_.width, + frame_info_.height, + rotation_mode, + origin_colorspace); + } + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, + controller_, + dst, + timestamp)); +} +#else +void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( + const uint8* data, + int length, + base::Time timestamp, + int rotation, + bool flip_vert, + bool flip_horiz) { + DCHECK(frame_info_.color == media::PIXEL_FORMAT_I420 || + frame_info_.color == media::PIXEL_FORMAT_YV12 || + frame_info_.color == media::PIXEL_FORMAT_NV21 || + (rotation == 0 && !flip_vert && !flip_horiz)); + + TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); + + if (!buffer_pool_) + return; + scoped_refptr<media::VideoFrame> dst = + buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, + frame_info_.height), + rotation); if (!dst.get()) return; @@ -287,26 +433,36 @@ void VideoCaptureController::OnIncomingCapturedFrame( // Do color conversion from the camera format to I420. switch (frame_info_.color) { - case media::VideoCaptureCapability::kColorUnknown: // Color format not set. + case media::PIXEL_FORMAT_UNKNOWN: // Color format not set. break; - case media::VideoCaptureCapability::kI420: + case media::PIXEL_FORMAT_I420: DCHECK(!chopped_width_ && !chopped_height_); RotatePackedYV12Frame( data, yplane, uplane, vplane, frame_info_.width, frame_info_.height, rotation, flip_vert, flip_horiz); break; - case media::VideoCaptureCapability::kYV12: + case media::PIXEL_FORMAT_YV12: DCHECK(!chopped_width_ && !chopped_height_); RotatePackedYV12Frame( data, yplane, vplane, uplane, frame_info_.width, frame_info_.height, rotation, flip_vert, flip_horiz); break; - case media::VideoCaptureCapability::kNV21: + case media::PIXEL_FORMAT_NV21: { DCHECK(!chopped_width_ && !chopped_height_); - media::ConvertNV21ToYUV(data, yplane, uplane, vplane, frame_info_.width, + int num_pixels = frame_info_.width * frame_info_.height; + media::ConvertNV21ToYUV(data, + &i420_intermediate_buffer_[0], + &i420_intermediate_buffer_[num_pixels], + &i420_intermediate_buffer_[num_pixels * 5 / 4], + frame_info_.width, frame_info_.height); - break; - case media::VideoCaptureCapability::kYUY2: + RotatePackedYV12Frame( + i420_intermediate_buffer_.get(), yplane, uplane, vplane, + frame_info_.width, frame_info_.height, + rotation, flip_vert, flip_horiz); + break; + } + case media::PIXEL_FORMAT_YUY2: DCHECK(!chopped_width_ && !chopped_height_); if (frame_info_.width * frame_info_.height * 2 != length) { // If |length| of |data| does not match the expected width and height @@ -317,42 +473,22 @@ void VideoCaptureController::OnIncomingCapturedFrame( media::ConvertYUY2ToYUV(data, yplane, uplane, vplane, frame_info_.width, frame_info_.height); break; - case media::VideoCaptureCapability::kRGB24: { + case media::PIXEL_FORMAT_RGB24: { int ystride = frame_info_.width; int uvstride = frame_info_.width / 2; -#if defined(OS_WIN) // RGB on Windows start at the bottom line. - int rgb_stride = -3 * (frame_info_.width + chopped_width_); - const uint8* rgb_src = data + 3 * (frame_info_.width + chopped_width_) * - (frame_info_.height -1 + chopped_height_); -#else int rgb_stride = 3 * (frame_info_.width + chopped_width_); const uint8* rgb_src = data; -#endif media::ConvertRGB24ToYUV(rgb_src, yplane, uplane, vplane, frame_info_.width, frame_info_.height, rgb_stride, ystride, uvstride); break; } - case media::VideoCaptureCapability::kARGB: + case media::PIXEL_FORMAT_ARGB: media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width, frame_info_.height, (frame_info_.width + chopped_width_) * 4, frame_info_.width, frame_info_.width / 2); break; -#if !defined(OS_IOS) && !defined(OS_ANDROID) - case media::VideoCaptureCapability::kMJPEG: { - int yplane_stride = frame_info_.width; - int uv_plane_stride = (frame_info_.width + 1) / 2; - int crop_x = 0; - int crop_y = 0; - libyuv::ConvertToI420(data, length, yplane, yplane_stride, uplane, - uv_plane_stride, vplane, uv_plane_stride, crop_x, - crop_y, frame_info_.width, frame_info_.height, - frame_info_.width, frame_info_.height, - libyuv::kRotate0, libyuv::FOURCC_MJPG); - break; - } -#endif default: NOTREACHED(); } @@ -360,38 +496,35 @@ void VideoCaptureController::OnIncomingCapturedFrame( BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, - this, dst, timestamp)); + controller_, dst, timestamp)); } +#endif // #if !defined(OS_IOS) && !defined(OS_ANDROID) -// OnIncomingCapturedVideoFrame is called the thread running the capture device. -void VideoCaptureController::OnIncomingCapturedVideoFrame( +void +VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( const scoped_refptr<media::VideoFrame>& frame, base::Time timestamp) { + if (!buffer_pool_) + return; - scoped_refptr<media::VideoFrame> target; - { - base::AutoLock lock(buffer_pool_lock_); - - if (!buffer_pool_.get()) - return; - - // If this is a frame that belongs to the buffer pool, we can forward it - // directly to the IO thread and be done. - if (buffer_pool_->RecognizeReservedBuffer( - frame->shared_memory_handle()) >= 0) { - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, - this, frame, timestamp)); - return; - } - // Otherwise, this is a frame that belongs to the caller, and we must copy - // it to a frame from the buffer pool. - target = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, - frame_info_.height), - 0); + // If this is a frame that belongs to the buffer pool, we can forward it + // directly to the IO thread and be done. + if (buffer_pool_->RecognizeReservedBuffer( + frame->shared_memory_handle()) >= 0) { + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, + controller_, frame, timestamp)); + return; } + // Otherwise, this is a frame that belongs to the caller, and we must copy + // it to a frame from the buffer pool. + scoped_refptr<media::VideoFrame> target = + buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, + frame_info_.height), + 0); + if (!target.get()) return; @@ -478,18 +611,18 @@ void VideoCaptureController::OnIncomingCapturedVideoFrame( BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, - this, target, timestamp)); + controller_, target, timestamp)); } -void VideoCaptureController::OnError() { +void VideoCaptureController::VideoCaptureDeviceClient::OnError() { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureController::DoErrorOnIOThread, this)); + base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); } -void VideoCaptureController::OnFrameInfo( +void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfo( const media::VideoCaptureCapability& info) { - frame_info_= info; + frame_info_ = info; // Handle cases when |info| has odd numbers for width/height. if (info.width & 1) { --frame_info_.width; @@ -503,32 +636,51 @@ void VideoCaptureController::OnFrameInfo( } else { chopped_height_ = 0; } +#if defined(OS_IOS) || defined(OS_ANDROID) + if (frame_info_.color == media::PIXEL_FORMAT_NV21 && + !i420_intermediate_buffer_) { + i420_intermediate_buffer_.reset( + new uint8[frame_info_.width * frame_info_.height * 12 / 8]); + } +#endif // #if defined(OS_IOS) || defined(OS_ANDROID) + + DCHECK(!buffer_pool_.get()); + + // TODO(nick): Give BufferPool the same lifetime as the controller, have it + // support frame size changes, and stop checking it for NULL everywhere. + // http://crbug.com/266082 + buffer_pool_ = new VideoCaptureBufferPool( + media::VideoFrame::AllocationSize( + media::VideoFrame::I420, + gfx::Size(frame_info_.width, frame_info_.height)), + kNoOfBuffers); + + // Check whether all buffers were created successfully. + if (!buffer_pool_->Allocate()) { + // Transition to the error state. + buffer_pool_ = NULL; + OnError(); + return; + } + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, this)); + base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, controller_, + frame_info_, buffer_pool_)); } -void VideoCaptureController::OnFrameInfoChanged( +void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfoChanged( const media::VideoCaptureCapability& info) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureController::DoFrameInfoChangedOnIOThread, - this, info)); + controller_, info)); } VideoCaptureController::~VideoCaptureController() { buffer_pool_ = NULL; // Release all buffers. STLDeleteContainerPointers(controller_clients_.begin(), controller_clients_.end()); - STLDeleteContainerPointers(pending_clients_.begin(), - pending_clients_.end()); -} - -// Called by VideoCaptureManager when a device have been stopped. -void VideoCaptureController::OnDeviceStopped() { - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureController::DoDeviceStoppedOnIOThread, this)); } void VideoCaptureController::DoIncomingCapturedFrameOnIOThread( @@ -563,37 +715,24 @@ void VideoCaptureController::DoIncomingCapturedFrameOnIOThread( buffer_pool_->HoldForConsumers(buffer_id, count); } -void VideoCaptureController::DoFrameInfoOnIOThread() { +void VideoCaptureController::DoFrameInfoOnIOThread( + const media::VideoCaptureCapability& frame_info, + const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(!buffer_pool_.get()) - << "Device is restarted without releasing shared memory."; + DCHECK(!buffer_pool_.get()) << "Frame info should happen only once."; // Allocate memory only when device has been started. if (state_ != VIDEO_CAPTURE_STATE_STARTED) return; - scoped_refptr<VideoCaptureBufferPool> buffer_pool = - new VideoCaptureBufferPool(frame_info_.width * frame_info_.height * 3 / 2, - kNoOfBuffers); - - // Check whether all buffers were created successfully. - if (!buffer_pool->Allocate()) { - state_ = VIDEO_CAPTURE_STATE_ERROR; - for (ControllerClients::iterator client_it = controller_clients_.begin(); - client_it != controller_clients_.end(); ++client_it) { - (*client_it)->event_handler->OnError((*client_it)->controller_id); - } - return; - } - - { - base::AutoLock lock(buffer_pool_lock_); - buffer_pool_ = buffer_pool; - } - frame_info_available_ = true; + frame_info_ = frame_info; + buffer_pool_ = buffer_pool; for (ControllerClients::iterator client_it = controller_clients_.begin(); client_it != controller_clients_.end(); ++client_it) { + if ((*client_it)->session_closed) + continue; + SendFrameInfoAndBuffers(*client_it); } } @@ -605,6 +744,9 @@ void VideoCaptureController::DoFrameInfoChangedOnIOThread( // needed, to support the new video capture format. See crbug.com/266082. for (ControllerClients::iterator client_it = controller_clients_.begin(); client_it != controller_clients_.end(); ++client_it) { + if ((*client_it)->session_closed) + continue; + (*client_it)->event_handler->OnFrameInfoChanged( (*client_it)->controller_id, info.width, @@ -619,25 +761,16 @@ void VideoCaptureController::DoErrorOnIOThread() { ControllerClients::iterator client_it; for (client_it = controller_clients_.begin(); client_it != controller_clients_.end(); ++client_it) { - (*client_it)->event_handler->OnError((*client_it)->controller_id); - } - for (client_it = pending_clients_.begin(); - client_it != pending_clients_.end(); ++client_it) { - (*client_it)->event_handler->OnError((*client_it)->controller_id); - } -} + if ((*client_it)->session_closed) + continue; -void VideoCaptureController::DoDeviceStoppedOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - device_in_use_ = false; - if (state_ == VIDEO_CAPTURE_STATE_STOPPING) { - PostStopping(); + (*client_it)->event_handler->OnError((*client_it)->controller_id); } } void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(frame_info_available_); + DCHECK(frame_info_.IsValid()); client->event_handler->OnFrameInfo(client->controller_id, frame_info_); for (int buffer_id = 0; buffer_id < buffer_pool_->count(); ++buffer_id) { @@ -679,54 +812,9 @@ VideoCaptureController::FindClient( return NULL; } -// This function is called when all buffers have been returned to controller, -// or when device is stopped. It decides whether the device needs to be -// restarted. -void VideoCaptureController::PostStopping() { +int VideoCaptureController::GetClientCount() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPING); - - // When clients still have some buffers, or device has not been stopped yet, - // do nothing. - if ((buffer_pool_.get() && buffer_pool_->IsAnyBufferHeldForConsumers()) || - device_in_use_) - return; - - { - base::AutoLock lock(buffer_pool_lock_); - buffer_pool_ = NULL; - } - - // No more client. Therefore the controller is stopped. - if (controller_clients_.empty() && pending_clients_.empty()) { - state_ = VIDEO_CAPTURE_STATE_STOPPED; - return; - } - - // Restart the device. - current_params_.width = 0; - current_params_.height = 0; - ControllerClients::iterator client_it; - for (client_it = controller_clients_.begin(); - client_it != controller_clients_.end(); ++client_it) { - if (current_params_.width < (*client_it)->parameters.width) - current_params_.width = (*client_it)->parameters.width; - if (current_params_.height < (*client_it)->parameters.height) - current_params_.height = (*client_it)->parameters.height; - } - for (client_it = pending_clients_.begin(); - client_it != pending_clients_.end(); ) { - if (current_params_.width < (*client_it)->parameters.width) - current_params_.width = (*client_it)->parameters.width; - if (current_params_.height < (*client_it)->parameters.height) - current_params_.height = (*client_it)->parameters.height; - controller_clients_.push_back((*client_it)); - pending_clients_.erase(client_it++); - } - // Request the manager to start the actual capture. - video_capture_manager_->Start(current_params_, this); - state_ = VIDEO_CAPTURE_STATE_STARTED; - device_in_use_ = true; + return controller_clients_.size(); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller.h b/chromium/content/browser/renderer_host/media/video_capture_controller.h index 5d33d01163c..eda4ce31e01 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller.h +++ b/chromium/content/browser/renderer_host/media/video_capture_controller.h @@ -1,17 +1,48 @@ // Copyright (c) 2012 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. - -// VideoCaptureController is the glue between VideoCaptureHost, -// VideoCaptureManager and VideoCaptureDevice. -// It provides functions for VideoCaptureHost to start a VideoCaptureDevice and -// is responsible for keeping track of shared DIBs and filling them with I420 -// video frames for IPC communication between VideoCaptureHost and -// VideoCaptureMessageFilter. -// It implements media::VideoCaptureDevice::EventHandler to get video frames -// from a VideoCaptureDevice object and do color conversion straight into the -// shared DIBs to avoid a memory copy. -// It serves multiple VideoCaptureControllerEventHandlers. +// +// VideoCaptureController is the glue between a VideoCaptureDevice and all +// VideoCaptureHosts that have connected to it. A controller exists on behalf of +// one (and only one) VideoCaptureDevice; both are owned by the +// VideoCaptureManager. +// +// The VideoCaptureController is responsible for: +// +// * Allocating and keeping track of shared memory buffers, and filling them +// with I420 video frames for IPC communication between VideoCaptureHost (in +// the browser process) and VideoCaptureMessageFilter (in the renderer +// process). +// * Broadcasting the events from a single VideoCaptureDevice, fanning them +// out to multiple clients. +// * Keeping track of the clients on behalf of the VideoCaptureManager, making +// it possible for the Manager to delete the Controller and its Device when +// there are no clients left. +// +// A helper class, VCC::VideoCaptureDeviceClient, is responsible for: +// +// * Conveying events from the device thread (where VideoCaptureDevices live) +// the IO thread (where the VideoCaptureController lives). +// * Performing some image transformations on the output of the Device; +// specifically, colorspace conversion and rotation. +// +// Interactions between VideoCaptureController and other classes: +// +// * VideoCaptureController indirectly observes a VideoCaptureDevice +// by means of its proxy, VideoCaptureDeviceClient, which implements +// the VideoCaptureDevice::EventHandler interface. The proxy forwards +// observed events to the VideoCaptureController on the IO thread. +// * A VideoCaptureController interacts with its clients (VideoCaptureHosts) +// via the VideoCaptureControllerEventHandler interface. +// * Conversely, a VideoCaptureControllerEventHandler (typically, +// VideoCaptureHost) will interact directly with VideoCaptureController to +// return leased buffers by means of the ReturnBuffer() public method of +// VCC. +// * VideoCaptureManager (which owns the VCC) interacts directly with +// VideoCaptureController through its public methods, to add and remove +// clients. +// +// VideoCaptureController is not thread safe and operates on the IO thread only. #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_CONTROLLER_H_ @@ -21,6 +52,8 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/process/process.h" #include "base/synchronization/lock.h" #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" @@ -32,31 +65,37 @@ #include "media/video/capture/video_capture_types.h" namespace content { -class VideoCaptureManager; class VideoCaptureBufferPool; -class CONTENT_EXPORT VideoCaptureController - : public base::RefCountedThreadSafe<VideoCaptureController>, - public media::VideoCaptureDevice::EventHandler { +class CONTENT_EXPORT VideoCaptureController { public: - VideoCaptureController(VideoCaptureManager* video_capture_manager); + VideoCaptureController(); + virtual ~VideoCaptureController(); + + base::WeakPtr<VideoCaptureController> GetWeakPtr(); + + // Return a new VideoCaptureDeviceClient to forward capture events to this + // instance. + scoped_ptr<media::VideoCaptureDevice::EventHandler> NewDeviceClient(); // Start video capturing and try to use the resolution specified in // |params|. - // When capturing has started, the |event_handler| receives a call OnFrameInfo - // with resolution that best matches the requested that the video - // capture device support. - void StartCapture(const VideoCaptureControllerID& id, - VideoCaptureControllerEventHandler* event_handler, - base::ProcessHandle render_process, - const media::VideoCaptureParams& params); - - // Stop video capture. - // This will take back all buffers held by by |event_handler|, and - // |event_handler| shouldn't use those buffers any more. - void StopCapture(const VideoCaptureControllerID& id, + // When capturing starts, the |event_handler| will receive an OnFrameInfo() + // call informing it of the resolution that was actually picked by the device. + void AddClient(const VideoCaptureControllerID& id, + VideoCaptureControllerEventHandler* event_handler, + base::ProcessHandle render_process, + const media::VideoCaptureParams& params); + + // Stop video capture. This will take back all buffers held by by + // |event_handler|, and |event_handler| shouldn't use those buffers any more. + // Returns the session_id of the stopped client, or + // kInvalidMediaCaptureSessionId if the indicated client was not registered. + int RemoveClient(const VideoCaptureControllerID& id, VideoCaptureControllerEventHandler* event_handler); + int GetClientCount(); + // API called directly by VideoCaptureManager in case the device is // prematurely closed. void StopSession(int session_id); @@ -67,39 +106,19 @@ class CONTENT_EXPORT VideoCaptureController VideoCaptureControllerEventHandler* event_handler, int buffer_id); - // Implement media::VideoCaptureDevice::EventHandler. - virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE; - virtual void OnIncomingCapturedFrame(const uint8* data, - int length, - base::Time timestamp, - int rotation, - bool flip_vert, - bool flip_horiz) OVERRIDE; - virtual void OnIncomingCapturedVideoFrame( - const scoped_refptr<media::VideoFrame>& frame, - base::Time timestamp) OVERRIDE; - virtual void OnError() OVERRIDE; - virtual void OnFrameInfo(const media::VideoCaptureCapability& info) OVERRIDE; - virtual void OnFrameInfoChanged( - const media::VideoCaptureCapability& info) OVERRIDE; - - protected: - virtual ~VideoCaptureController(); - private: - friend class base::RefCountedThreadSafe<VideoCaptureController>; + class VideoCaptureDeviceClient; struct ControllerClient; typedef std::list<ControllerClient*> ControllerClients; - // Callback when manager has stopped device. - void OnDeviceStopped(); - - // Worker functions on IO thread. + // Worker functions on IO thread. Called by the VideoCaptureDeviceClient. void DoIncomingCapturedFrameOnIOThread( const scoped_refptr<media::VideoFrame>& captured_frame, base::Time timestamp); - void DoFrameInfoOnIOThread(); + void DoFrameInfoOnIOThread( + const media::VideoCaptureCapability& frame_info, + const scoped_refptr<VideoCaptureBufferPool>& buffer_pool); void DoFrameInfoChangedOnIOThread(const media::VideoCaptureCapability& info); void DoErrorOnIOThread(); void DoDeviceStoppedOnIOThread(); @@ -118,45 +137,25 @@ class CONTENT_EXPORT VideoCaptureController int session_id, const ControllerClients& clients); - // Decide what to do after kStopping state. Dependent on events, controller - // can stay in kStopping state, or go to kStopped, or restart capture. - void PostStopping(); - - // Protects access to the |buffer_pool_| pointer on non-IO threads. IO thread - // must hold this lock when modifying the |buffer_pool_| pointer itself. - // TODO(nick): Make it so that this lock isn't required. - base::Lock buffer_pool_lock_; - // The pool of shared-memory buffers used for capturing. scoped_refptr<VideoCaptureBufferPool> buffer_pool_; // All clients served by this controller. ControllerClients controller_clients_; - // All clients waiting for service. - ControllerClients pending_clients_; - // The parameter that currently used for the capturing. media::VideoCaptureParams current_params_; - // It's modified on caller thread, assuming there is only one OnFrameInfo() - // call per StartCapture(). + // Tracks the current frame format. media::VideoCaptureCapability frame_info_; - // Chopped pixels in width/height in case video capture device has odd numbers - // for width/height. - int chopped_width_; - int chopped_height_; - - // It's accessed only on IO thread. - bool frame_info_available_; - - VideoCaptureManager* video_capture_manager_; - - bool device_in_use_; + // Takes on only the states 'STARTED' and 'ERROR'. 'ERROR' is an absorbing + // state which stops the flow of data to clients. VideoCaptureState state_; - DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureController); + base::WeakPtrFactory<VideoCaptureController> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(VideoCaptureController); }; } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h b/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h index c4844af2f73..4a2c294b0ba 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h +++ b/chromium/content/browser/renderer_host/media/video_capture_controller_event_handler.h @@ -10,7 +10,7 @@ #include "content/common/content_export.h" namespace media { -struct VideoCaptureCapability; +class VideoCaptureCapability; } namespace content { diff --git a/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc index c4b716d2e33..fec0942dde0 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_controller_unittest.cc @@ -7,141 +7,74 @@ #include <string> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" -#include "content/browser/browser_thread_impl.h" +#include "base/run_loop.h" #include "content/browser/renderer_host/media/media_stream_provider.h" #include "content/browser/renderer_host/media/video_capture_controller.h" +#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/common/media/media_stream_options.h" -#include "media/video/capture/fake_video_capture_device.h" -#include "media/video/capture/video_capture_device.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "media/video/capture/video_capture_types.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::AtLeast; using ::testing::InSequence; -using ::testing::Return; +using ::testing::Mock; namespace content { -enum { kDeviceId = 1 }; - -ACTION_P4(StopCapture, controller, controller_id, controller_handler, - message_loop) { - message_loop->PostTask(FROM_HERE, - base::Bind(&VideoCaptureController::StopCapture, - controller, controller_id, controller_handler)); - message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); -} - -ACTION_P3(StopSession, controller, session_id, message_loop) { - message_loop->PostTask(FROM_HERE, - base::Bind(&VideoCaptureController::StopSession, - controller, session_id)); - message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); -} - class MockVideoCaptureControllerEventHandler : public VideoCaptureControllerEventHandler { public: - MockVideoCaptureControllerEventHandler(VideoCaptureController* controller, - base::MessageLoop* message_loop) - : controller_(controller), - message_loop_(message_loop), - controller_id_(kDeviceId), - process_handle_(base::kNullProcessHandle) { - } + explicit MockVideoCaptureControllerEventHandler( + VideoCaptureController* controller) + : controller_(controller) {} virtual ~MockVideoCaptureControllerEventHandler() {} + // These mock methods are delegated to by our fake implementation of + // VideoCaptureControllerEventHandler, to be used in EXPECT_CALL(). MOCK_METHOD1(DoBufferCreated, void(const VideoCaptureControllerID&)); MOCK_METHOD1(DoBufferReady, void(const VideoCaptureControllerID&)); MOCK_METHOD1(DoFrameInfo, void(const VideoCaptureControllerID&)); MOCK_METHOD1(DoEnded, void(const VideoCaptureControllerID&)); + MOCK_METHOD1(DoError, void(const VideoCaptureControllerID&)); - virtual void OnError(const VideoCaptureControllerID& id) OVERRIDE {} + virtual void OnError(const VideoCaptureControllerID& id) OVERRIDE { + DoError(id); + } virtual void OnBufferCreated(const VideoCaptureControllerID& id, base::SharedMemoryHandle handle, int length, int buffer_id) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoBufferCreated(id); } virtual void OnBufferReady(const VideoCaptureControllerID& id, int buffer_id, base::Time timestamp) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoBufferReady(id); - message_loop_->PostTask(FROM_HERE, + base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&VideoCaptureController::ReturnBuffer, - controller_, controller_id_, this, buffer_id)); + base::Unretained(controller_), id, this, buffer_id)); } virtual void OnFrameInfo( const VideoCaptureControllerID& id, const media::VideoCaptureCapability& format) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoFrameInfo(id); } virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE { - EXPECT_EQ(id, controller_id_); DoEnded(id); + // OnEnded() must respond by (eventually) unregistering the client. + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(base::IgnoreResult(&VideoCaptureController::RemoveClient), + base::Unretained(controller_), id, this)); } - scoped_refptr<VideoCaptureController> controller_; - base::MessageLoop* message_loop_; - VideoCaptureControllerID controller_id_; - base::ProcessHandle process_handle_; -}; - -class MockVideoCaptureManager : public VideoCaptureManager { - public: - MockVideoCaptureManager() - : video_session_id_(kStartOpenSessionId), - device_name_("fake_device_0", "/dev/video0") {} - - void Init() { - video_capture_device_.reset( - media::FakeVideoCaptureDevice::Create(device_name_)); - ASSERT_TRUE(video_capture_device_.get() != NULL); - } - - MOCK_METHOD3(StartCapture, void(int, int, - media::VideoCaptureDevice::EventHandler*)); - MOCK_METHOD1(StopCapture, void(const media::VideoCaptureSessionId&)); - - void Start(const media::VideoCaptureParams& capture_params, - media::VideoCaptureDevice::EventHandler* vc_receiver) OVERRIDE { - StartCapture(capture_params.width, capture_params.height, vc_receiver); - // TODO(mcasas): Add testing for variable resolution video capture devices, - // supported by FakeVideoCaptureDevice. See crbug.com/261410, second part. - media::VideoCaptureCapability capture_format( - capture_params.width, - capture_params.height, - capture_params.frame_per_second, - media::VideoCaptureCapability::kI420, - 0, - false, - media::ConstantResolutionVideoCaptureDevice); - video_capture_device_->Allocate(capture_format, vc_receiver); - video_capture_device_->Start(); - } - - void Stop(const media::VideoCaptureSessionId& capture_session_id, - base::Closure stopped_cb) OVERRIDE { - StopCapture(capture_session_id); - video_capture_device_->Stop(); - video_capture_device_->DeAllocate(); - } - - int video_session_id_; - media::VideoCaptureDevice::Name device_name_; - scoped_ptr<media::VideoCaptureDevice> video_capture_device_; - - private: - virtual ~MockVideoCaptureManager() {} - DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureManager); + VideoCaptureController* controller_; }; // Test class. @@ -151,115 +84,371 @@ class VideoCaptureControllerTest : public testing::Test { virtual ~VideoCaptureControllerTest() {} protected: + static const int kPoolSize = 3; + virtual void SetUp() OVERRIDE { - message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO)); - file_thread_.reset(new BrowserThreadImpl(BrowserThread::FILE, - message_loop_.get())); - io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO, - message_loop_.get())); - - vcm_ = new MockVideoCaptureManager(); - vcm_->Init(); - controller_ = new VideoCaptureController(vcm_.get()); - controller_handler_.reset(new MockVideoCaptureControllerEventHandler( - controller_.get(), message_loop_.get())); + controller_.reset(new VideoCaptureController()); + device_ = controller_->NewDeviceClient().Pass(); + client_a_.reset(new MockVideoCaptureControllerEventHandler( + controller_.get())); + client_b_.reset(new MockVideoCaptureControllerEventHandler( + controller_.get())); } - virtual void TearDown() OVERRIDE {} + virtual void TearDown() OVERRIDE { + base::RunLoop().RunUntilIdle(); + } - scoped_ptr<base::MessageLoop> message_loop_; - scoped_ptr<BrowserThreadImpl> file_thread_; - scoped_ptr<BrowserThreadImpl> io_thread_; - scoped_refptr<MockVideoCaptureManager> vcm_; - scoped_ptr<MockVideoCaptureControllerEventHandler> controller_handler_; - scoped_refptr<VideoCaptureController> controller_; + TestBrowserThreadBundle bindle_; + scoped_ptr<MockVideoCaptureControllerEventHandler> client_a_; + scoped_ptr<MockVideoCaptureControllerEventHandler> client_b_; + scoped_ptr<VideoCaptureController> controller_; + scoped_ptr<media::VideoCaptureDevice::EventHandler> device_; private: DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest); }; -// Try to start and stop capture. -TEST_F(VideoCaptureControllerTest, StartAndStop) { - media::VideoCaptureParams capture_params; - capture_params.session_id = vcm_->video_session_id_; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - - InSequence s; - EXPECT_CALL(*vcm_.get(), - StartCapture(capture_params.width, - capture_params.height, - controller_.get())).Times(1); - EXPECT_CALL(*controller_handler_, - DoFrameInfo(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferCreated(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferReady(controller_handler_->controller_id_)) - .Times(AtLeast(1)) - .WillOnce(StopCapture(controller_.get(), - controller_handler_->controller_id_, - controller_handler_.get(), - message_loop_.get())); - EXPECT_CALL(*vcm_.get(), StopCapture(vcm_->video_session_id_)).Times(1); - - controller_->StartCapture(controller_handler_->controller_id_, - controller_handler_.get(), - controller_handler_->process_handle_, - capture_params); - message_loop_->Run(); +// A simple test of VideoCaptureController's ability to add, remove, and keep +// track of clients. +TEST_F(VideoCaptureControllerTest, AddAndRemoveClients) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + media::VideoCaptureParams session_300 = session_100; + session_300.session_id = 300; + + media::VideoCaptureParams session_400 = session_100; + session_400.session_id = 400; + + // Intentionally use the same route ID for two of the clients: the device_ids + // are a per-VideoCaptureHost namespace, and can overlap across hosts. + const VideoCaptureControllerID client_a_route_1(44); + const VideoCaptureControllerID client_a_route_2(30); + const VideoCaptureControllerID client_b_route_1(30); + const VideoCaptureControllerID client_b_route_2(1); + + // Clients in controller: [] + ASSERT_EQ(0, controller_->GetClientCount()) + << "Client count should initially be zero."; + controller_->AddClient(client_a_route_1, client_a_.get(), + base::kNullProcessHandle, session_100); + // Clients in controller: [A/1] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Adding client A/1 should bump client count.";; + controller_->AddClient(client_a_route_2, client_a_.get(), + base::kNullProcessHandle, session_200); + // Clients in controller: [A/1, A/2] + ASSERT_EQ(2, controller_->GetClientCount()) + << "Adding client A/2 should bump client count."; + controller_->AddClient(client_b_route_1, client_b_.get(), + base::kNullProcessHandle, session_300); + // Clients in controller: [A/1, A/2, B/1] + ASSERT_EQ(3, controller_->GetClientCount()) + << "Adding client B/1 should bump client count."; + ASSERT_EQ(200, + controller_->RemoveClient(client_a_route_2, client_a_.get())) + << "Removing client A/1 should return its session_id."; + // Clients in controller: [A/1, B/1] + ASSERT_EQ(2, controller_->GetClientCount()); + ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId), + controller_->RemoveClient(client_a_route_2, client_a_.get())) + << "Removing a nonexistant client should fail."; + // Clients in controller: [A/1, B/1] + ASSERT_EQ(2, controller_->GetClientCount()); + ASSERT_EQ(300, + controller_->RemoveClient(client_b_route_1, client_b_.get())) + << "Removing client B/1 should return its session_id."; + // Clients in controller: [A/1] + ASSERT_EQ(1, controller_->GetClientCount()); + controller_->AddClient(client_b_route_2, client_b_.get(), + base::kNullProcessHandle, session_400); + // Clients in controller: [A/1, B/2] + + EXPECT_CALL(*client_a_, DoEnded(client_a_route_1)).Times(1); + controller_->StopSession(100); // Session 100 == client A/1 + Mock::VerifyAndClearExpectations(client_a_.get()); + ASSERT_EQ(2, controller_->GetClientCount()) + << "Client should be closed but still exist after StopSession."; + // Clients in controller: [A/1 (closed, removal pending), B/2] + base::RunLoop().RunUntilIdle(); + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Client A/1 should be deleted by now."; + controller_->StopSession(200); // Session 200 does not exist anymore + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Stopping non-existant session 200 should be a no-op."; + controller_->StopSession(256); // Session 256 never existed. + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Stopping non-existant session 256 should be a no-op."; + ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId), + controller_->RemoveClient(client_a_route_1, client_a_.get())) + << "Removing already-removed client A/1 should fail."; + // Clients in controller: [B/2] + ASSERT_EQ(1, controller_->GetClientCount()) + << "Removing non-existant session 200 should be a no-op."; + ASSERT_EQ(400, + controller_->RemoveClient(client_b_route_2, client_b_.get())) + << "Removing client B/2 should return its session_id."; + // Clients in controller: [] + ASSERT_EQ(0, controller_->GetClientCount()) + << "Client count should return to zero after all clients are gone."; +} + +// This test will connect and disconnect several clients while simulating an +// active capture device being started and generating frames. It runs on one +// thread and is intended to behave deterministically. +TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + media::VideoCaptureParams session_300 = session_100; + session_300.session_id = 300; + + // session_id of 1 is kStartOpenSessionId, which should have special meaning + // to VideoCaptureManager, but not to VideoCaptureController ... so test it. + media::VideoCaptureParams session_1 = session_100; + session_1.session_id = VideoCaptureManager::kStartOpenSessionId; + + // The device format needn't match the VideoCaptureParams (the camera can do + // what it wants). Pick something random to use for OnFrameInfo. + media::VideoCaptureCapability device_format( + 10, 10, 25, media::PIXEL_FORMAT_RGB24, 10, false, + media::ConstantResolutionVideoCaptureDevice); + + const VideoCaptureControllerID client_a_route_1(0xa1a1a1a1); + const VideoCaptureControllerID client_a_route_2(0xa2a2a2a2); + const VideoCaptureControllerID client_b_route_1(0xb1b1b1b1); + const VideoCaptureControllerID client_b_route_2(0xb2b2b2b2); + + // Start with two clients. + controller_->AddClient(client_a_route_1, client_a_.get(), + base::kNullProcessHandle, session_100); + controller_->AddClient(client_b_route_1, client_b_.get(), + base::kNullProcessHandle, session_300); + ASSERT_EQ(2, controller_->GetClientCount()); + + // The OnFrameInfo() event from the device, when processed by the controller, + // should generate client OnFrameInfo() and OnBufferCreated() events. + { + InSequence s; + EXPECT_CALL(*client_a_, DoFrameInfo(client_a_route_1)).Times(1); + EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1)).Times(kPoolSize); + } + { + InSequence s; + EXPECT_CALL(*client_b_, DoFrameInfo(client_b_route_1)).Times(1); + EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1)).Times(kPoolSize); + } + device_->OnFrameInfo(device_format); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // When a third clients is subsequently added, the frame info and buffers + // should immediately be shared to the new clients. + { + InSequence s; + EXPECT_CALL(*client_a_, DoFrameInfo(client_a_route_2)).Times(1); + EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(kPoolSize); + } + controller_->AddClient(client_a_route_2, client_a_.get(), + base::kNullProcessHandle, session_200); + Mock::VerifyAndClearExpectations(client_a_.get()); + + // Now, simulate an incoming captured frame from the capture device. + uint8 frame_no = 1; + scoped_refptr<media::VideoFrame> frame; + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + frame = NULL; + + // The buffer should be delivered to the clients in any order. + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // Second frame. In this case pretend that the VideoFrame pointer is held + // by the device for a long delay. This shouldn't affect anything. + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + + // The buffer should be delivered to the clients in any order. + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + frame = NULL; + + // Add a fourth client now that some frames have come through. It should get + // the buffer info, but it won't get any frames until new ones are captured. + { + InSequence s; + EXPECT_CALL(*client_b_, DoFrameInfo(client_b_route_2)).Times(1); + EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_2)).Times(kPoolSize); + } + controller_->AddClient(client_b_route_2, client_b_.get(), + base::kNullProcessHandle, session_1); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // Third, fourth, and fifth frames. Pretend they all arrive at the same time. + for (int i = 0; i < kPoolSize; i++) { + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + ASSERT_EQ(media::VideoFrame::I420, frame->format()); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + + } + // ReserveOutputBuffer ought to fail now, because the pool is depleted. + ASSERT_FALSE(device_->ReserveOutputBuffer()); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(kPoolSize); + EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(kPoolSize); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(kPoolSize); + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(kPoolSize); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // Now test the interaction of client shutdown and frame delivery. + // Kill A1 via renderer disconnect (synchronous). + controller_->RemoveClient(client_a_route_1, client_a_.get()); + // Kill B1 via session close (posts a task to disconnect). + EXPECT_CALL(*client_b_, DoEnded(client_b_route_1)).Times(1); + controller_->StopSession(300); + // Queue up another frame. + frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + frame = device_->ReserveOutputBuffer(); + { + // Kill A2 via session close (posts a task to disconnect, but A2 must not + // be sent either of these two frames).. + EXPECT_CALL(*client_a_, DoEnded(client_a_route_2)).Times(1); + controller_->StopSession(200); + } + ASSERT_TRUE(frame); + media::FillYUV(frame, frame_no++, 0x22, 0x44); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + // B2 is the only client left, and is the only one that should + // get the frame. + EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(2); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + Mock::VerifyAndClearExpectations(client_b_.get()); +} + +// Exercises the OnError() codepath of VideoCaptureController, and tests the +// behavior of various operations after the error state has been signalled. +TEST_F(VideoCaptureControllerTest, ErrorBeforeDeviceCreation) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + const VideoCaptureControllerID route_id(0x99); + + // Start with one client. + controller_->AddClient(route_id, client_a_.get(), + base::kNullProcessHandle, session_100); + device_->OnError(); + EXPECT_CALL(*client_a_, DoError(route_id)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + + // Second client connects after the error state. It also should get told of + // the error. + EXPECT_CALL(*client_b_, DoError(route_id)).Times(1); + controller_->AddClient(route_id, client_b_.get(), + base::kNullProcessHandle, session_200); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_b_.get()); + + // OnFrameInfo from the VCD should become a no-op after the error occurs. + media::VideoCaptureCapability device_format( + 10, 10, 25, media::PIXEL_FORMAT_ARGB, 10, false, + media::ConstantResolutionVideoCaptureDevice); + + device_->OnFrameInfo(device_format); + base::RunLoop().RunUntilIdle(); } -// Try to stop session before stopping capture. -TEST_F(VideoCaptureControllerTest, StopSession) { - media::VideoCaptureParams capture_params; - capture_params.session_id = vcm_->video_session_id_; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - - InSequence s; - EXPECT_CALL(*vcm_.get(), - StartCapture(capture_params.width, - capture_params.height, - controller_.get())).Times(1); - EXPECT_CALL(*controller_handler_, - DoFrameInfo(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferCreated(controller_handler_->controller_id_)) - .Times(AtLeast(1)); - EXPECT_CALL(*controller_handler_, - DoBufferReady(controller_handler_->controller_id_)) - .Times(AtLeast(1)) - .WillOnce(StopSession(controller_.get(), - vcm_->video_session_id_, - message_loop_.get())); - EXPECT_CALL(*controller_handler_, - DoEnded(controller_handler_->controller_id_)) - .Times(1); - - controller_->StartCapture(controller_handler_->controller_id_, - controller_handler_.get(), - controller_handler_->process_handle_, - capture_params); - message_loop_->Run(); - - // The session is stopped now. There should be no buffer coming from - // controller. - EXPECT_CALL(*controller_handler_, - DoBufferReady(controller_handler_->controller_id_)) - .Times(0); - message_loop_->PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitClosure(), base::TimeDelta::FromSeconds(1)); - message_loop_->Run(); - - EXPECT_CALL(*vcm_.get(), StopCapture(vcm_->video_session_id_)).Times(1); - controller_->StopCapture(controller_handler_->controller_id_, - controller_handler_.get()); +// Exercises the OnError() codepath of VideoCaptureController, and tests the +// behavior of various operations after the error state has been signalled. +TEST_F(VideoCaptureControllerTest, ErrorAfterDeviceCreation) { + media::VideoCaptureParams session_100; + session_100.session_id = 100; + session_100.width = 320; + session_100.height = 240; + session_100.frame_rate = 30; + + media::VideoCaptureParams session_200 = session_100; + session_200.session_id = 200; + + const VideoCaptureControllerID route_id(0x99); + + // Start with one client. + controller_->AddClient(route_id, client_a_.get(), + base::kNullProcessHandle, session_100); + // OnFrameInfo from the VCD should become a no-op after the error occurs. + media::VideoCaptureCapability device_format( + 10, 10, 25, media::PIXEL_FORMAT_ARGB, 10, false, + media::ConstantResolutionVideoCaptureDevice); + + // Start the device and get as far as exchanging buffers with the subprocess. + // Then, signal an error and deliver the frame. The error should be propagated + // to clients; the frame should not be. + device_->OnFrameInfo(device_format); + EXPECT_CALL(*client_a_, DoFrameInfo(route_id)).Times(1); + EXPECT_CALL(*client_a_, DoBufferCreated(route_id)).Times(kPoolSize); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + + scoped_refptr<media::VideoFrame> frame = device_->ReserveOutputBuffer(); + ASSERT_TRUE(frame); + + device_->OnError(); + device_->OnIncomingCapturedVideoFrame(frame, base::Time()); + frame = NULL; + + EXPECT_CALL(*client_a_, DoError(route_id)).Times(1); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(client_a_.get()); + + // Second client connects after the error state. It also should get told of + // the error. + EXPECT_CALL(*client_b_, DoError(route_id)).Times(1); + controller_->AddClient(route_id, client_b_.get(), + base::kNullProcessHandle, session_200); + Mock::VerifyAndClearExpectations(client_b_.get()); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_host.cc b/chromium/content/browser/renderer_host/media/video_capture_host.cc index bc7c8c19d7d..7ed77ae53d3 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_host.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_host.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" -#include "base/stl_util.h" #include "content/browser/browser_main_loop.h" #include "content/browser/renderer_host/media/media_stream_manager.h" #include "content/browser/renderer_host/media/video_capture_manager.h" @@ -14,15 +13,6 @@ namespace content { -struct VideoCaptureHost::Entry { - Entry(VideoCaptureController* controller) - : controller(controller) {} - - ~Entry() {} - - scoped_refptr<VideoCaptureController> controller; -}; - VideoCaptureHost::VideoCaptureHost(MediaStreamManager* media_stream_manager) : media_stream_manager_(media_stream_manager) { } @@ -32,17 +22,15 @@ VideoCaptureHost::~VideoCaptureHost() {} void VideoCaptureHost::OnChannelClosing() { BrowserMessageFilter::OnChannelClosing(); - // Since the IPC channel is gone, close all requested VideCaptureDevices. + // Since the IPC channel is gone, close all requested VideoCaptureDevices. for (EntryMap::iterator it = entries_.begin(); it != entries_.end(); it++) { - VideoCaptureController* controller = it->second->controller.get(); + const base::WeakPtr<VideoCaptureController>& controller = it->second; if (controller) { VideoCaptureControllerID controller_id(it->first); - controller->StopCapture(controller_id, this); - media_stream_manager_->video_capture_manager()->RemoveController( - controller, this); + media_stream_manager_->video_capture_manager()->StopCaptureForClient( + controller.get(), controller_id, this); } } - STLDeleteValues(&entries_); } void VideoCaptureHost::OnDestruct() const { @@ -95,11 +83,11 @@ void VideoCaptureHost::OnFrameInfoChanged( const VideoCaptureControllerID& controller_id, int width, int height, - int frame_per_second) { + int frame_rate) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureHost::DoSendFrameInfoChangedOnIOThread, - this, controller_id, width, height, frame_per_second)); + this, controller_id, width, height, frame_rate)); } void VideoCaptureHost::OnEnded(const VideoCaptureControllerID& controller_id) { @@ -170,7 +158,7 @@ void VideoCaptureHost::DoSendFrameInfoOnIOThread( media::VideoCaptureParams params; params.width = format.width; params.height = format.height; - params.frame_per_second = format.frame_rate; + params.frame_rate = format.frame_rate; params.frame_size_type = format.frame_size_type; Send(new VideoCaptureMsg_DeviceInfo(controller_id.device_id, params)); Send(new VideoCaptureMsg_StateChanged(controller_id.device_id, @@ -181,7 +169,7 @@ void VideoCaptureHost::DoSendFrameInfoChangedOnIOThread( const VideoCaptureControllerID& controller_id, int width, int height, - int frame_per_second) { + int frame_rate) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (entries_.find(controller_id) == entries_.end()) @@ -190,7 +178,7 @@ void VideoCaptureHost::DoSendFrameInfoChangedOnIOThread( media::VideoCaptureParams params; params.width = width; params.height = height; - params.frame_per_second = frame_per_second; + params.frame_rate = frame_rate; Send(new VideoCaptureMsg_DeviceInfoChanged(controller_id.device_id, params)); } @@ -215,7 +203,7 @@ void VideoCaptureHost::OnStartCapture(int device_id, DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DVLOG(1) << "VideoCaptureHost::OnStartCapture, device_id " << device_id << ", (" << params.width << ", " << params.height << ", " - << params.frame_per_second << ", " << params.session_id + << params.frame_rate << ", " << params.session_id << ", variable resolution device:" << ((params.frame_size_type == media::VariableResolutionVideoCaptureDevice) ? "yes" : "no") @@ -223,45 +211,44 @@ void VideoCaptureHost::OnStartCapture(int device_id, VideoCaptureControllerID controller_id(device_id); DCHECK(entries_.find(controller_id) == entries_.end()); - entries_[controller_id] = new Entry(NULL); - media_stream_manager_->video_capture_manager()->AddController( - params, this, base::Bind(&VideoCaptureHost::OnControllerAdded, this, - device_id, params)); + entries_[controller_id] = base::WeakPtr<VideoCaptureController>(); + media_stream_manager_->video_capture_manager()->StartCaptureForClient( + params, PeerHandle(), controller_id, this, base::Bind( + &VideoCaptureHost::OnControllerAdded, this, device_id, params)); } void VideoCaptureHost::OnControllerAdded( int device_id, const media::VideoCaptureParams& params, - VideoCaptureController* controller) { + const base::WeakPtr<VideoCaptureController>& controller) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&VideoCaptureHost::DoControllerAddedOnIOThread, - this, device_id, params, make_scoped_refptr(controller))); + this, device_id, params, controller)); } void VideoCaptureHost::DoControllerAddedOnIOThread( int device_id, const media::VideoCaptureParams params, - VideoCaptureController* controller) { + const base::WeakPtr<VideoCaptureController>& controller) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); VideoCaptureControllerID controller_id(device_id); EntryMap::iterator it = entries_.find(controller_id); if (it == entries_.end()) { if (controller) { - media_stream_manager_->video_capture_manager()->RemoveController( - controller, this); + media_stream_manager_->video_capture_manager()->StopCaptureForClient( + controller.get(), controller_id, this); } return; } - if (controller == NULL) { + if (!controller) { Send(new VideoCaptureMsg_StateChanged(device_id, VIDEO_CAPTURE_STATE_ERROR)); - delete it->second; entries_.erase(controller_id); return; } - it->second->controller = controller; - controller->StartCapture(controller_id, this, PeerHandle(), params); + DCHECK(!it->second); + it->second = controller; } void VideoCaptureHost::OnStopCapture(int device_id) { @@ -288,8 +275,8 @@ void VideoCaptureHost::OnReceiveEmptyBuffer(int device_id, int buffer_id) { VideoCaptureControllerID controller_id(device_id); EntryMap::iterator it = entries_.find(controller_id); if (it != entries_.end()) { - scoped_refptr<VideoCaptureController> controller = it->second->controller; - if (controller.get()) + const base::WeakPtr<VideoCaptureController>& controller = it->second; + if (controller) controller->ReturnBuffer(controller_id, this, buffer_id); } } @@ -302,14 +289,11 @@ void VideoCaptureHost::DeleteVideoCaptureControllerOnIOThread( if (it == entries_.end()) return; - VideoCaptureController* controller = it->second->controller.get(); - if (controller) { - controller->StopCapture(controller_id, this); - media_stream_manager_->video_capture_manager()->RemoveController( - controller, this); + if (it->second) { + media_stream_manager_->video_capture_manager()->StopCaptureForClient( + it->second.get(), controller_id, this); } - delete it->second; - entries_.erase(controller_id); + entries_.erase(it); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_host.h b/chromium/content/browser/renderer_host/media/video_capture_host.h index 025b849de94..6ce395631eb 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_host.h +++ b/chromium/content/browser/renderer_host/media/video_capture_host.h @@ -39,6 +39,7 @@ #include <map> #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner_helpers.h" #include "content/browser/renderer_host/media/video_capture_controller.h" #include "content/common/content_export.h" @@ -46,7 +47,7 @@ #include "ipc/ipc_message.h" namespace media { -struct VideoCaptureCapability; +class VideoCaptureCapability; } namespace content { @@ -97,10 +98,10 @@ class CONTENT_EXPORT VideoCaptureHost const media::VideoCaptureParams& params); void OnControllerAdded( int device_id, const media::VideoCaptureParams& params, - VideoCaptureController* controller); + const base::WeakPtr<VideoCaptureController>& controller); void DoControllerAddedOnIOThread( int device_id, const media::VideoCaptureParams params, - VideoCaptureController* controller); + const base::WeakPtr<VideoCaptureController>& controller); // IPC message: Stop capture on device referenced by |device_id|. void OnStopCapture(int device_id); @@ -148,9 +149,12 @@ class CONTENT_EXPORT VideoCaptureHost MediaStreamManager* media_stream_manager_; - struct Entry; - typedef std::map<VideoCaptureControllerID, Entry*> EntryMap; - // A map of VideoCaptureControllerID to its state and VideoCaptureController. + typedef std::map<VideoCaptureControllerID, + base::WeakPtr<VideoCaptureController> > EntryMap; + + // A map of VideoCaptureControllerID to the VideoCaptureController to which it + // is connected. An entry in this map holds a null controller while it is in + // the process of starting. EntryMap entries_; DISALLOW_COPY_AND_ASSIGN(VideoCaptureHost); diff --git a/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc index 762148c86a2..ca1891dc426 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_host_unittest.cc @@ -20,6 +20,7 @@ #include "content/public/test/mock_resource_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "media/audio/audio_manager.h" +#include "media/base/video_frame.h" #include "media/video/capture/video_capture_types.h" #include "net/url_request/url_request_context.h" #include "testing/gmock/include/gmock/gmock.h" @@ -36,10 +37,10 @@ using ::testing::Return; namespace content { // Id used to identify the capture session between renderer and -// video_capture_host. -static const int kDeviceId = 1; +// video_capture_host. This is an arbitrary value. +static const int kDeviceId = 555; // Id of a video capture device -static const media::VideoCaptureSessionId kTestFakeDeviceId = +static const media::VideoCaptureSessionId kTestFakeSessionId = VideoCaptureManager::kStartOpenSessionId; // Define to enable test where video is dumped to file. @@ -57,7 +58,8 @@ class DumpVideo { base::FilePath file_name = base::FilePath(base::StringPrintf( FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height)); file_.reset(file_util::OpenFile(file_name, "wb")); - expected_size_ = width * height * 3 / 2; + expected_size_ = media::VideoFrame::AllocationSize( + media::VideoFrame::I420, gfx::Size(width, height)); } void NewVideoFrame(const void* buffer) { if (file_.get() != NULL) { @@ -146,7 +148,8 @@ class MockVideoCaptureHost : public VideoCaptureHost { // These handler methods do minimal things and delegate to the mock methods. void OnNewBufferCreatedDispatch(int device_id, base::SharedMemoryHandle handle, - int length, int buffer_id) { + uint32 length, + int buffer_id) { OnNewBufferCreated(device_id, handle, length, buffer_id); base::SharedMemory* dib = new base::SharedMemory(handle, false); dib->Map(length); @@ -249,14 +252,31 @@ class VideoCaptureHostTest : public testing::Test { media::VideoCaptureParams params; params.width = 352; params.height = 288; - params.frame_per_second = 30; - params.session_id = kTestFakeDeviceId; + params.frame_rate = 30; + params.session_id = kTestFakeSessionId; host_->OnStartCapture(kDeviceId, params); run_loop.Run(); } + void StartStopCapture() { + // Quickly start and then stop capture, without giving much chance for + // asynchronous start operations to complete. + InSequence s; + base::RunLoop run_loop; + EXPECT_CALL(*host_.get(), + OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)); + media::VideoCaptureParams params; + params.width = 352; + params.height = 288; + params.frame_rate = 30; + params.session_id = kTestFakeSessionId; + host_->OnStartCapture(kDeviceId, params); + host_->OnStopCapture(kDeviceId); + run_loop.RunUntilIdle(); + } + #ifdef DUMP_VIDEO - void CaptureAndDumpVideo(int width, int heigt, int frame_rate) { + void CaptureAndDumpVideo(int width, int height, int frame_rate) { InSequence s; // 1. First - get info about the new resolution EXPECT_CALL(*host_, OnDeviceInfo(kDeviceId)); @@ -272,9 +292,9 @@ class VideoCaptureHostTest : public testing::Test { media::VideoCaptureParams params; params.width = width; - params.height = heigt; - params.frame_per_second = frame_rate; - params.session_id = kTestFakeDeviceId; + params.height = height; + params.frame_rate = frame_rate; + params.session_id = kTestFakeSessionId; host_->SetDumpVideo(true); host_->OnStartCapture(kDeviceId, params); run_loop.Run(); @@ -336,6 +356,12 @@ TEST_F(VideoCaptureHostTest, StartCapture) { StartCapture(); } +// Disabled because of a sometimes race between completion of implicit device +// enumeration and the capture stop. http://crbug.com/289684 +TEST_F(VideoCaptureHostTest, DISABLED_StopWhileStartOpening) { + StartStopCapture(); +} + TEST_F(VideoCaptureHostTest, StartCapturePlayStop) { StartCapture(); NotifyPacketReady(); diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager.cc b/chromium/content/browser/renderer_host/media/video_capture_manager.cc index 79da41260a3..f61aa83d093 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_manager.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_manager.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/logging.h" +#include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/threading/sequenced_worker_pool.h" #include "content/browser/renderer_host/media/video_capture_controller.h" @@ -33,20 +34,15 @@ namespace content { // explicitly calling open device. enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 }; -struct VideoCaptureManager::Controller { - Controller( - VideoCaptureController* vc_controller, - VideoCaptureControllerEventHandler* handler) - : controller(vc_controller), - ready_to_delete(false) { - handlers.push_front(handler); - } - ~Controller() {} +VideoCaptureManager::DeviceEntry::DeviceEntry( + MediaStreamType stream_type, + const std::string& id, + scoped_ptr<VideoCaptureController> controller) + : stream_type(stream_type), + id(id), + video_capture_controller(controller.Pass()) {} - scoped_refptr<VideoCaptureController> controller; - bool ready_to_delete; - Handlers handlers; -}; +VideoCaptureManager::DeviceEntry::~DeviceEntry() {} VideoCaptureManager::VideoCaptureManager() : listener_(NULL), @@ -56,7 +52,6 @@ VideoCaptureManager::VideoCaptureManager() VideoCaptureManager::~VideoCaptureManager() { DCHECK(devices_.empty()); - DCHECK(controllers_.empty()); } void VideoCaptureManager::Register(MediaStreamProviderListener* listener, @@ -75,109 +70,87 @@ void VideoCaptureManager::Unregister() { void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type; DCHECK(listener_); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnEnumerateDevices, this, stream_type)); + base::PostTaskAndReplyWithResult( + device_loop_, FROM_HERE, + base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, this, + stream_type), + base::Bind(&VideoCaptureManager::OnDevicesEnumerated, this, stream_type)); } -int VideoCaptureManager::Open(const StreamDeviceInfo& device) { +int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(listener_); - // Generate a new id for this device. - int video_capture_session_id = new_capture_session_id_++; + // Generate a new id for the session being opened. + const int capture_session_id = new_capture_session_id_++; - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnOpen, this, video_capture_session_id, - device)); + DCHECK(sessions_.find(capture_session_id) == sessions_.end()); + DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id; - return video_capture_session_id; + // We just save the stream info for processing later. + sessions_[capture_session_id] = device_info.device; + + // Notify our listener asynchronously; this ensures that we return + // |capture_session_id| to the caller of this function before using that same + // id in a listener event. + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&VideoCaptureManager::OnOpened, this, + device_info.device.type, capture_session_id)); + return capture_session_id; } void VideoCaptureManager::Close(int capture_session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(listener_); DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id; - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnClose, this, capture_session_id)); -} -void VideoCaptureManager::Start( - const media::VideoCaptureParams& capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnStart, this, capture_params, - video_capture_receiver)); -} + std::map<int, MediaStreamDevice>::iterator session_it = + sessions_.find(capture_session_id); + if (session_it == sessions_.end()) { + NOTREACHED(); + return; + } -void VideoCaptureManager::Stop( - const media::VideoCaptureSessionId& capture_session_id, - base::Closure stopped_cb) { - DVLOG(1) << "VideoCaptureManager::Stop, id " << capture_session_id; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::OnStop, this, capture_session_id, - stopped_cb)); + DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice( + session_it->second); + if (existing_device) { + // Remove any client that is still using the session. This is safe to call + // even if there are no clients using the session. + existing_device->video_capture_controller->StopSession(capture_session_id); + + // StopSession() may have removed the last client, so we might need to + // close the device. + DestroyDeviceEntryIfNoClients(existing_device); + } + + // Notify listeners asynchronously, and forget the session. + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type, + capture_session_id)); + sessions_.erase(session_it); } void VideoCaptureManager::UseFakeDevice() { use_fake_device_ = true; } -void VideoCaptureManager::OnEnumerateDevices(MediaStreamType stream_type) { - SCOPED_UMA_HISTOGRAM_TIMER( - "Media.VideoCaptureManager.OnEnumerateDevicesTime"); - DCHECK(IsOnDeviceThread()); - - media::VideoCaptureDevice::Names device_names; - GetAvailableDevices(stream_type, &device_names); - - scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray()); - for (media::VideoCaptureDevice::Names::iterator it = - device_names.begin(); it != device_names.end(); ++it) { - bool opened = DeviceOpened(*it); - devices->push_back(StreamDeviceInfo( - stream_type, it->GetNameAndModel(), it->id(), opened)); - } - - PostOnDevicesEnumerated(stream_type, devices.Pass()); -} - -void VideoCaptureManager::OnOpen(int capture_session_id, - const StreamDeviceInfo& device) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnOpenTime"); +void VideoCaptureManager::DoStartDeviceOnDeviceThread( + DeviceEntry* entry, + const media::VideoCaptureCapability& capture_params, + scoped_ptr<media::VideoCaptureDevice::EventHandler> device_client) { + SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); DCHECK(IsOnDeviceThread()); - DCHECK(devices_.find(capture_session_id) == devices_.end()); - DVLOG(1) << "VideoCaptureManager::OnOpen, id " << capture_session_id; - - // Check if another session has already opened this device. If so, just - // use that opened device. - media::VideoCaptureDevice* opened_video_capture_device = - GetOpenedDevice(device); - if (opened_video_capture_device) { - DeviceEntry& new_entry = devices_[capture_session_id]; - new_entry.stream_type = device.device.type; - new_entry.capture_device = opened_video_capture_device; - PostOnOpened(device.device.type, capture_session_id); - return; - } scoped_ptr<media::VideoCaptureDevice> video_capture_device; - - // Open the device. - switch (device.device.type) { + switch (entry->stream_type) { case MEDIA_DEVICE_VIDEO_CAPTURE: { // We look up the device id from the renderer in our local enumeration // since the renderer does not have all the information that might be // held in the browser-side VideoCaptureDevice::Name structure. media::VideoCaptureDevice::Name* found = - video_capture_devices_.FindById(device.device.id); + video_capture_devices_.FindById(entry->id); if (found) { video_capture_device.reset(use_fake_device_ ? media::FakeVideoCaptureDevice::Create(*found) : @@ -187,12 +160,12 @@ void VideoCaptureManager::OnOpen(int capture_session_id, } case MEDIA_TAB_VIDEO_CAPTURE: { video_capture_device.reset( - WebContentsVideoCaptureDevice::Create(device.device.id)); + WebContentsVideoCaptureDevice::Create(entry->id)); break; } case MEDIA_DESKTOP_VIDEO_CAPTURE: { #if defined(ENABLE_SCREEN_CAPTURE) - DesktopMediaID id = DesktopMediaID::Parse(device.device.id); + DesktopMediaID id = DesktopMediaID::Parse(entry->id); if (id.type != DesktopMediaID::TYPE_NONE) { video_capture_device = DesktopCaptureDevice::Create(id); } @@ -206,128 +179,123 @@ void VideoCaptureManager::OnOpen(int capture_session_id, } if (!video_capture_device) { - PostOnError(capture_session_id, kDeviceNotAvailable); + device_client->OnError(); return; } - DeviceEntry& new_entry = devices_[capture_session_id]; - new_entry.stream_type = device.device.type; - new_entry.capture_device = video_capture_device.release(); - PostOnOpened(device.device.type, capture_session_id); + video_capture_device->AllocateAndStart(capture_params, device_client.Pass()); + entry->video_capture_device = video_capture_device.Pass(); } -void VideoCaptureManager::OnClose(int capture_session_id) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnCloseTime"); - DCHECK(IsOnDeviceThread()); - DVLOG(1) << "VideoCaptureManager::OnClose, id " << capture_session_id; - - VideoCaptureDevices::iterator device_it = devices_.find(capture_session_id); - if (device_it == devices_.end()) { +void VideoCaptureManager::StartCaptureForClient( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, (" + << capture_params.width + << ", " << capture_params.height + << ", " << capture_params.frame_rate + << ", #" << capture_params.session_id + << ")"; + + if (capture_params.session_id == kStartOpenSessionId) { + // Solution for not using MediaStreamManager. Enumerate the devices and + // open the first one, and then start it. + base::PostTaskAndReplyWithResult(device_loop_, FROM_HERE, + base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, + this, MEDIA_DEVICE_VIDEO_CAPTURE), + base::Bind(&VideoCaptureManager::OpenAndStartDefaultSession, this, + capture_params, client_render_process, client_id, + client_handler, done_cb)); return; + } else { + DoStartCaptureForClient(capture_params, client_render_process, client_id, + client_handler, done_cb); } - const DeviceEntry removed_entry = device_it->second; - devices_.erase(device_it); - - Controllers::iterator cit = controllers_.find(removed_entry.capture_device); - if (cit != controllers_.end()) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureController::StopSession, - cit->second->controller, capture_session_id)); - } +} - if (!DeviceInUse(removed_entry.capture_device)) { - // No other users of this device, deallocate (if not done already) and - // delete the device. No need to take care of the controller, that is done - // by |OnStop|. - removed_entry.capture_device->DeAllocate(); - Controllers::iterator cit = controllers_.find(removed_entry.capture_device); - if (cit != controllers_.end()) { - delete cit->second; - controllers_.erase(cit); - } - delete removed_entry.capture_device; +void VideoCaptureManager::DoStartCaptureForClient( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + DeviceEntry* entry = GetOrCreateDeviceEntry(capture_params.session_id); + if (!entry) { + done_cb.Run(base::WeakPtr<VideoCaptureController>()); + return; } - PostOnClosed(removed_entry.stream_type, capture_session_id); -} + DCHECK(entry->video_capture_controller); -void VideoCaptureManager::OnStart( - const media::VideoCaptureParams capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStartTime"); - DCHECK(IsOnDeviceThread()); - DCHECK(video_capture_receiver != NULL); - DVLOG(1) << "VideoCaptureManager::OnStart, (" << capture_params.width - << ", " << capture_params.height - << ", " << capture_params.frame_per_second - << ", " << capture_params.session_id - << ")"; - - media::VideoCaptureDevice* video_capture_device = - GetDeviceInternal(capture_params.session_id); - if (!video_capture_device) { - // Invalid session id. - video_capture_receiver->OnError(); - return; + // First client starts the device. + if (entry->video_capture_controller->GetClientCount() == 0) { + DVLOG(1) << "VideoCaptureManager starting device (type = " + << entry->stream_type << ", id = " << entry->id << ")"; + + media::VideoCaptureCapability params_as_capability; + params_as_capability.width = capture_params.width; + params_as_capability.height = capture_params.height; + params_as_capability.frame_rate = capture_params.frame_rate; + params_as_capability.session_id = capture_params.session_id; + params_as_capability.frame_size_type = capture_params.frame_size_type; + + device_loop_->PostTask(FROM_HERE, base::Bind( + &VideoCaptureManager::DoStartDeviceOnDeviceThread, this, + entry, params_as_capability, + base::Passed(entry->video_capture_controller->NewDeviceClient()))); } - // TODO(mcasas): Variable resolution video capture devices, are not yet - // fully supported, see crbug.com/261410, second part, and crbug.com/266082 . - if (capture_params.frame_size_type != - media::ConstantResolutionVideoCaptureDevice) { - LOG(DFATAL) << "Only constant Video Capture resolution device supported."; - video_capture_receiver->OnError(); + // Run the callback first, as AddClient() may trigger OnFrameInfo(). + done_cb.Run(entry->video_capture_controller->GetWeakPtr()); + entry->video_capture_controller->AddClient(client_id, + client_handler, + client_render_process, + capture_params); +} + +void VideoCaptureManager::StopCaptureForClient( + VideoCaptureController* controller, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(controller); + DCHECK(client_handler); + + DeviceEntry* entry = GetDeviceEntryForController(controller); + if (!entry) { + NOTREACHED(); return; } - Controllers::iterator cit = controllers_.find(video_capture_device); - if (cit != controllers_.end()) { - cit->second->ready_to_delete = false; - } - // Possible errors are signaled to video_capture_receiver by - // video_capture_device. video_capture_receiver to perform actions. - media::VideoCaptureCapability params_as_capability_copy; - params_as_capability_copy.width = capture_params.width; - params_as_capability_copy.height = capture_params.height; - params_as_capability_copy.frame_rate = capture_params.frame_per_second; - params_as_capability_copy.session_id = capture_params.session_id; - params_as_capability_copy.frame_size_type = capture_params.frame_size_type; - video_capture_device->Allocate(params_as_capability_copy, - video_capture_receiver); - video_capture_device->Start(); -} + // Detach client from controller. + int session_id = controller->RemoveClient(client_id, client_handler); + DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = " + << session_id; -void VideoCaptureManager::OnStop( - const media::VideoCaptureSessionId capture_session_id, - base::Closure stopped_cb) { - SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStopTime"); - DCHECK(IsOnDeviceThread()); - DVLOG(1) << "VideoCaptureManager::OnStop, id " << capture_session_id; - - VideoCaptureDevices::iterator it = devices_.find(capture_session_id); - if (it != devices_.end()) { - media::VideoCaptureDevice* video_capture_device = it->second.capture_device; - // Possible errors are signaled to video_capture_receiver by - // video_capture_device. video_capture_receiver to perform actions. - video_capture_device->Stop(); - video_capture_device->DeAllocate(); - Controllers::iterator cit = controllers_.find(video_capture_device); - if (cit != controllers_.end()) { - cit->second->ready_to_delete = true; - if (cit->second->handlers.empty()) { - delete cit->second; - controllers_.erase(cit); - } - } - } + // If controller has no more clients, delete controller and device. + DestroyDeviceEntryIfNoClients(entry); - if (!stopped_cb.is_null()) - stopped_cb.Run(); + // Close the session if it was auto-opened by StartCaptureForClient(). + if (session_id == kStartOpenSessionId) { + sessions_.erase(session_id); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&VideoCaptureManager::OnClosed, this, + MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); + } +} - if (capture_session_id == kStartOpenSessionId) { - // This device was opened from Start(), not Open(). Close it! - OnClose(capture_session_id); +void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { + SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); + DCHECK(IsOnDeviceThread()); + if (entry->video_capture_device) { + entry->video_capture_device->StopAndDeAllocate(); } + entry->video_capture_device.reset(); } void VideoCaptureManager::OnOpened(MediaStreamType stream_type, @@ -352,242 +320,173 @@ void VideoCaptureManager::OnClosed(MediaStreamType stream_type, void VideoCaptureManager::OnDevicesEnumerated( MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices) { + const media::VideoCaptureDevice::Names& device_names) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!listener_) { - // Listener has been removed. - return; - } - listener_->DevicesEnumerated(stream_type, *devices); -} -void VideoCaptureManager::OnError(MediaStreamType stream_type, - int capture_session_id, - MediaStreamProviderError error) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!listener_) { // Listener has been removed. return; } - listener_->Error(stream_type, capture_session_id, error); -} -void VideoCaptureManager::PostOnOpened( - MediaStreamType stream_type, int capture_session_id) { - DCHECK(IsOnDeviceThread()); - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureManager::OnOpened, this, - stream_type, capture_session_id)); -} - -void VideoCaptureManager::PostOnClosed( - MediaStreamType stream_type, int capture_session_id) { - DCHECK(IsOnDeviceThread()); - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureManager::OnClosed, this, - stream_type, capture_session_id)); -} - -void VideoCaptureManager::PostOnDevicesEnumerated( - MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices) { - DCHECK(IsOnDeviceThread()); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&VideoCaptureManager::OnDevicesEnumerated, - this, stream_type, base::Passed(&devices))); -} + // Transform from VCD::Name to StreamDeviceInfo. + StreamDeviceInfoArray devices; + for (media::VideoCaptureDevice::Names::const_iterator it = + device_names.begin(); it != device_names.end(); ++it) { + devices.push_back(StreamDeviceInfo( + stream_type, it->GetNameAndModel(), it->id(), false)); + } -void VideoCaptureManager::PostOnError(int capture_session_id, - MediaStreamProviderError error) { - DCHECK(IsOnDeviceThread()); - MediaStreamType stream_type = MEDIA_DEVICE_VIDEO_CAPTURE; - VideoCaptureDevices::const_iterator it = devices_.find(capture_session_id); - if (it != devices_.end()) - stream_type = it->second.stream_type; - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&VideoCaptureManager::OnError, this, - stream_type, capture_session_id, error)); + listener_->DevicesEnumerated(stream_type, devices); } bool VideoCaptureManager::IsOnDeviceThread() const { return device_loop_->BelongsToCurrentThread(); } -void VideoCaptureManager::GetAvailableDevices( - MediaStreamType stream_type, - media::VideoCaptureDevice::Names* device_names) { +media::VideoCaptureDevice::Names +VideoCaptureManager::GetAvailableDevicesOnDeviceThread( + MediaStreamType stream_type) { + SCOPED_UMA_HISTOGRAM_TIMER( + "Media.VideoCaptureManager.GetAvailableDevicesTime"); DCHECK(IsOnDeviceThread()); + media::VideoCaptureDevice::Names result; switch (stream_type) { case MEDIA_DEVICE_VIDEO_CAPTURE: // Cache the latest enumeration of video capture devices. // We'll refer to this list again in OnOpen to avoid having to // enumerate the devices again. - video_capture_devices_.clear(); if (!use_fake_device_) { - media::VideoCaptureDevice::GetDeviceNames(&video_capture_devices_); + media::VideoCaptureDevice::GetDeviceNames(&result); } else { - media::FakeVideoCaptureDevice::GetDeviceNames(&video_capture_devices_); + media::FakeVideoCaptureDevice::GetDeviceNames(&result); } - *device_names = video_capture_devices_; + + // TODO(nick): The correctness of device start depends on this cache being + // maintained, but it seems a little odd to keep a cache here. Can we + // eliminate it? + video_capture_devices_ = result; break; case MEDIA_DESKTOP_VIDEO_CAPTURE: - device_names->clear(); + // Do nothing. break; default: NOTREACHED(); break; } + return result; } -bool VideoCaptureManager::DeviceOpened( - const media::VideoCaptureDevice::Name& device_name) { - DCHECK(IsOnDeviceThread()); +VideoCaptureManager::DeviceEntry* +VideoCaptureManager::GetDeviceEntryForMediaStreamDevice( + const MediaStreamDevice& device_info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - for (VideoCaptureDevices::iterator it = devices_.begin(); + for (DeviceEntries::iterator it = devices_.begin(); it != devices_.end(); ++it) { - if (device_name.id() == it->second.capture_device->device_name().id()) { - // We've found the device! - return true; - } - } - return false; -} - -media::VideoCaptureDevice* VideoCaptureManager::GetOpenedDevice( - const StreamDeviceInfo& device_info) { - DCHECK(IsOnDeviceThread()); - - for (VideoCaptureDevices::iterator it = devices_.begin(); - it != devices_.end(); it++) { - if (device_info.device.id == - it->second.capture_device->device_name().id()) { - return it->second.capture_device; + DeviceEntry* device = *it; + if (device_info.type == device->stream_type && + device_info.id == device->id) { + return device; } } return NULL; } -bool VideoCaptureManager::DeviceInUse( - const media::VideoCaptureDevice* video_capture_device) { - DCHECK(IsOnDeviceThread()); - - for (VideoCaptureDevices::iterator it = devices_.begin(); +VideoCaptureManager::DeviceEntry* +VideoCaptureManager::GetDeviceEntryForController( + const VideoCaptureController* controller) { + // Look up |controller| in |devices_|. + for (DeviceEntries::iterator it = devices_.begin(); it != devices_.end(); ++it) { - if (video_capture_device == it->second.capture_device) { - // We've found the device! - return true; + if ((*it)->video_capture_controller.get() == controller) { + return *it; } } - return false; + return NULL; } -void VideoCaptureManager::AddController( +void VideoCaptureManager::OpenAndStartDefaultSession( const media::VideoCaptureParams& capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb) { - DCHECK(handler); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::DoAddControllerOnDeviceThread, - this, capture_params, handler, added_cb)); -} - -void VideoCaptureManager::DoAddControllerOnDeviceThread( - const media::VideoCaptureParams capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb) { - DCHECK(IsOnDeviceThread()); + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb, + const media::VideoCaptureDevice::Names& device_names) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - media::VideoCaptureDevice* video_capture_device = - GetDeviceInternal(capture_params.session_id); - scoped_refptr<VideoCaptureController> controller; - if (video_capture_device) { - Controllers::iterator cit = controllers_.find(video_capture_device); - if (cit == controllers_.end()) { - controller = new VideoCaptureController(this); - controllers_[video_capture_device] = - new Controller(controller.get(), handler); - } else { - controllers_[video_capture_device]->handlers.push_front(handler); - controller = controllers_[video_capture_device]->controller; - } + // |device_names| is a value returned by GetAvailableDevicesOnDeviceThread(). + // We'll mimic an Open() operation on the first element in that list. + DCHECK(capture_params.session_id == kStartOpenSessionId); + if (device_names.empty() || + sessions_.count(capture_params.session_id) != 0) { + done_cb.Run(base::WeakPtr<VideoCaptureController>()); + return; } - added_cb.Run(controller.get()); -} -void VideoCaptureManager::RemoveController( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler) { - DCHECK(handler); - device_loop_->PostTask( - FROM_HERE, - base::Bind(&VideoCaptureManager::DoRemoveControllerOnDeviceThread, this, - make_scoped_refptr(controller), handler)); -} + // Open the device by creating a |sessions_| entry. + sessions_[capture_params.session_id] = + MediaStreamDevice(MEDIA_DEVICE_VIDEO_CAPTURE, + device_names.front().id(), + device_names.front().GetNameAndModel()); -void VideoCaptureManager::DoRemoveControllerOnDeviceThread( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler) { - DCHECK(IsOnDeviceThread()); + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&VideoCaptureManager::OnOpened, this, + MEDIA_DEVICE_VIDEO_CAPTURE, kStartOpenSessionId)); - for (Controllers::iterator cit = controllers_.begin(); - cit != controllers_.end(); ++cit) { - if (controller == cit->second->controller.get()) { - Handlers& handlers = cit->second->handlers; - for (Handlers::iterator hit = handlers.begin(); - hit != handlers.end(); ++hit) { - if ((*hit) == handler) { - handlers.erase(hit); - break; - } - } - if (handlers.empty() && cit->second->ready_to_delete) { - delete cit->second; - controllers_.erase(cit); - } - return; - } - } + DoStartCaptureForClient(capture_params, client_render_process, client_id, + client_handler, done_cb); } -media::VideoCaptureDevice* VideoCaptureManager::GetDeviceInternal( - int capture_session_id) { - DCHECK(IsOnDeviceThread()); - VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); - if (dit != devices_.end()) { - return dit->second.capture_device; +void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Removal of the last client stops the device. + if (entry->video_capture_controller->GetClientCount() == 0) { + DVLOG(1) << "VideoCaptureManager stopping device (type = " + << entry->stream_type << ", id = " << entry->id << ")"; + + // The DeviceEntry is removed from |devices_| immediately. The controller is + // deleted immediately, and the device is freed asynchronously. After this + // point, subsequent requests to open this same device ID will create a new + // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. + devices_.erase(entry); + entry->video_capture_controller.reset(); + device_loop_->PostTask( + FROM_HERE, + base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, + base::Owned(entry))); } +} - // Solution for not using MediaStreamManager. - // This session id won't be returned by Open(). - if (capture_session_id == kStartOpenSessionId) { - media::VideoCaptureDevice::Names device_names; - GetAvailableDevices(MEDIA_DEVICE_VIDEO_CAPTURE, &device_names); - if (device_names.empty()) { - // No devices available. - return NULL; - } - StreamDeviceInfo device(MEDIA_DEVICE_VIDEO_CAPTURE, - device_names.front().GetNameAndModel(), - device_names.front().id(), - false); +VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( + int capture_session_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - // Call OnOpen to open using the first device in the list. - OnOpen(capture_session_id, device); + std::map<int, MediaStreamDevice>::iterator session_it = + sessions_.find(capture_session_id); + if (session_it == sessions_.end()) { + return NULL; + } + const MediaStreamDevice& device_info = session_it->second; - VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); - if (dit != devices_.end()) { - return dit->second.capture_device; - } + // Check if another session has already opened this device. If so, just + // use that opened device. + DeviceEntry* const existing_device = + GetDeviceEntryForMediaStreamDevice(device_info); + if (existing_device) { + DCHECK_EQ(device_info.type, existing_device->stream_type); + return existing_device; } - return NULL; + + scoped_ptr<VideoCaptureController> video_capture_controller( + new VideoCaptureController()); + DeviceEntry* new_device = new DeviceEntry(device_info.type, + device_info.id, + video_capture_controller.Pass()); + devices_.insert(new_device); + return new_device; } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager.h b/chromium/content/browser/renderer_host/media/video_capture_manager.h index 34d6e626413..fc6dd92fecd 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_manager.h +++ b/chromium/content/browser/renderer_host/media/video_capture_manager.h @@ -4,31 +4,39 @@ // VideoCaptureManager is used to open/close, start/stop, enumerate available // video capture devices, and manage VideoCaptureController's. -// All functions are expected to be called from Browser::IO thread. +// All functions are expected to be called from Browser::IO thread. Some helper +// functions (*OnDeviceThread) will dispatch operations to the device thread. // VideoCaptureManager will open OS dependent instances of VideoCaptureDevice. // A device can only be opened once. #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_MANAGER_H_ -#include <list> #include <map> +#include <set> +#include <string> #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/process/process_handle.h" #include "content/browser/renderer_host/media/media_stream_provider.h" +#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" #include "content/common/content_export.h" #include "content/common/media/media_stream_options.h" #include "media/video/capture/video_capture_device.h" #include "media/video/capture/video_capture_types.h" namespace content { -class MockVideoCaptureManager; class VideoCaptureController; class VideoCaptureControllerEventHandler; // VideoCaptureManager opens/closes and start/stops video capture devices. class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { public: + // Callback used to signal the completion of a controller lookup. + typedef base::Callback< + void(const base::WeakPtr<VideoCaptureController>&)> DoneCB; + // Calling |Start| of this id will open the first device, even though open has // not been called. This is used to be able to use video capture devices // before MediaStream is implemented in Chrome and WebKit. @@ -48,118 +56,147 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider { virtual void Close(int capture_session_id) OVERRIDE; - // Functions used to start and stop media flow. - // Start allocates the device and no other application can use the device - // before Stop is called. Captured video frames will be delivered to - // video_capture_receiver. - virtual void Start(const media::VideoCaptureParams& capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver); - - // Stops capture device referenced by |capture_session_id|. No more frames - // will be delivered to the frame receiver, and |stopped_cb| will be called. - // |stopped_cb| can be NULL. - virtual void Stop(const media::VideoCaptureSessionId& capture_session_id, - base::Closure stopped_cb); - // Used by unit test to make sure a fake device is used instead of a real // video capture device. Due to timing requirements, the function must be // called before EnumerateDevices and Open. void UseFakeDevice(); - // Called by VideoCaptureHost to get a controller for |capture_params|. - // The controller is returned via calling |added_cb|. - void AddController( - const media::VideoCaptureParams& capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb); - // Called by VideoCaptureHost to remove the |controller|. - void RemoveController( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler); + // Called by VideoCaptureHost to locate a capture device for |capture_params|, + // adding the Host as a client of the device's controller if successful. The + // value of |capture_params.session_id| controls which device is selected; + // this value should be a session id previously returned by Open(). + // + // If the device is not already started (i.e., no other client is currently + // capturing from this device), this call will cause a VideoCaptureController + // and VideoCaptureDevice to be created, possibly asynchronously. + // + // On success, the controller is returned via calling |done_cb|, indicating + // that the client was successfully added. A NULL controller is passed to + // the callback on failure. + void StartCaptureForClient(const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb); + + // Called by VideoCaptureHost to remove |client_handler|. If this is the last + // client of the device, the |controller| and its VideoCaptureDevice may be + // destroyed. The client must not access |controller| after calling this + // function. + void StopCaptureForClient(VideoCaptureController* controller, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler); private: - friend class MockVideoCaptureManager; - virtual ~VideoCaptureManager(); + struct DeviceEntry; - typedef std::list<VideoCaptureControllerEventHandler*> Handlers; - struct Controller; - - // Called by the public functions, executed on device thread. - void OnEnumerateDevices(MediaStreamType stream_type); - void OnOpen(int capture_session_id, const StreamDeviceInfo& device); - void OnClose(int capture_session_id); - void OnStart(const media::VideoCaptureParams capture_params, - media::VideoCaptureDevice::EventHandler* video_capture_receiver); - void OnStop(const media::VideoCaptureSessionId capture_session_id, - base::Closure stopped_cb); - void DoAddControllerOnDeviceThread( - const media::VideoCaptureParams capture_params, - VideoCaptureControllerEventHandler* handler, - base::Callback<void(VideoCaptureController*)> added_cb); - void DoRemoveControllerOnDeviceThread( - VideoCaptureController* controller, - VideoCaptureControllerEventHandler* handler); - - // Executed on Browser::IO thread to call Listener. + // Helper for the kStartOpenSessionId case. + void OpenAndStartDefaultSession( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb, + const media::VideoCaptureDevice::Names& device_names); + + // Helper routine implementing StartCaptureForClient(). + void DoStartCaptureForClient( + const media::VideoCaptureParams& capture_params, + base::ProcessHandle client_render_process, + VideoCaptureControllerID client_id, + VideoCaptureControllerEventHandler* client_handler, + const DoneCB& done_cb); + + // Check to see if |entry| has no clients left on its controller. If so, + // remove it from the list of devices, and delete it asynchronously. |entry| + // may be freed by this function. + void DestroyDeviceEntryIfNoClients(DeviceEntry* entry); + + // Helpers to report an event to our Listener. void OnOpened(MediaStreamType type, int capture_session_id); void OnClosed(MediaStreamType type, int capture_session_id); void OnDevicesEnumerated(MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices); - void OnError(MediaStreamType type, int capture_session_id, - MediaStreamProviderError error); - - // Executed on device thread to make sure Listener is called from - // Browser::IO thread. - void PostOnOpened(MediaStreamType type, int capture_session_id); - void PostOnClosed(MediaStreamType type, int capture_session_id); - void PostOnDevicesEnumerated(MediaStreamType stream_type, - scoped_ptr<StreamDeviceInfoArray> devices); - void PostOnError(int capture_session_id, MediaStreamProviderError error); - - // Helpers - void GetAvailableDevices(MediaStreamType stream_type, - media::VideoCaptureDevice::Names* device_names); - bool DeviceOpened(const media::VideoCaptureDevice::Name& device_name); - bool DeviceInUse(const media::VideoCaptureDevice* video_capture_device); - media::VideoCaptureDevice* GetOpenedDevice( - const StreamDeviceInfo& device_info); + const media::VideoCaptureDevice::Names& names); + + // Find a DeviceEntry by its device ID and type, if it is already opened. + DeviceEntry* GetDeviceEntryForMediaStreamDevice( + const MediaStreamDevice& device_info); + + // Find a DeviceEntry entry for the indicated session, creating a fresh one + // if necessary. Returns NULL if the session id is invalid. + DeviceEntry* GetOrCreateDeviceEntry(int capture_session_id); + + // Find the DeviceEntry that owns a particular controller pointer. + DeviceEntry* GetDeviceEntryForController( + const VideoCaptureController* controller); + bool IsOnDeviceThread() const; - media::VideoCaptureDevice* GetDeviceInternal(int capture_session_id); - // The message loop of media stream device thread that this object runs on. + // Queries and returns the available device IDs. + media::VideoCaptureDevice::Names GetAvailableDevicesOnDeviceThread( + MediaStreamType stream_type); + + // Create and Start a new VideoCaptureDevice, storing the result in + // |entry->video_capture_device|. Ownership of |handler| passes to + // the device. + void DoStartDeviceOnDeviceThread( + DeviceEntry* entry, + const media::VideoCaptureCapability& capture_params, + scoped_ptr<media::VideoCaptureDevice::EventHandler> handler); + + // Stop and destroy the VideoCaptureDevice held in + // |entry->video_capture_device|. + void DoStopDeviceOnDeviceThread(DeviceEntry* entry); + + // The message loop of media stream device thread, where VCD's live. scoped_refptr<base::MessageLoopProxy> device_loop_; // Only accessed on Browser::IO thread. MediaStreamProviderListener* listener_; int new_capture_session_id_; - // Only accessed from device thread. - // VideoCaptureManager owns all VideoCaptureDevices and is responsible for - // deleting the instances when they are not used any longer. + // An entry is kept in this map for every session that has been created via + // the Open() entry point. The keys are session_id's. This map is used to + // determine which device to use when StartCaptureForClient() occurs. Used + // only on the IO thread. + std::map<int, MediaStreamDevice> sessions_; + + // An entry, kept in a map, that owns a VideoCaptureDevice and its associated + // VideoCaptureController. VideoCaptureManager owns all VideoCaptureDevices + // and VideoCaptureControllers and is responsible for deleting the instances + // when they are not used any longer. + // + // The set of currently started VideoCaptureDevice and VideoCaptureController + // objects is only accessed from IO thread, though the DeviceEntry instances + // themselves may visit to the device thread for device creation and + // destruction. struct DeviceEntry { - MediaStreamType stream_type; - media::VideoCaptureDevice* capture_device; // Maybe shared across sessions. + DeviceEntry(MediaStreamType stream_type, + const std::string& id, + scoped_ptr<VideoCaptureController> controller); + ~DeviceEntry(); + + const MediaStreamType stream_type; + const std::string id; + + // The controller. Only used from the IO thread. + scoped_ptr<VideoCaptureController> video_capture_controller; + + // The capture device. Only used from the device thread. + scoped_ptr<media::VideoCaptureDevice> video_capture_device; }; - typedef std::map<int, DeviceEntry> VideoCaptureDevices; - VideoCaptureDevices devices_; // Maps capture_session_id to DeviceEntry. + typedef std::set<DeviceEntry*> DeviceEntries; + DeviceEntries devices_; - // Set to true if using fake video capture devices for testing, - // false by default. This is only used for the MEDIA_DEVICE_VIDEO_CAPTURE - // device type. + // Set to true if using fake video capture devices for testing, false by + // default. This is only used for the MEDIA_DEVICE_VIDEO_CAPTURE device type. bool use_fake_device_; - // Only accessed from device thread. - // VideoCaptureManager owns all VideoCaptureController's and is responsible - // for deleting the instances when they are not used any longer. - // VideoCaptureDevice is one-to-one mapped to VideoCaptureController. - typedef std::map<media::VideoCaptureDevice*, Controller*> Controllers; - Controllers controllers_; - - // We cache the enumerated video capture devices in GetAvailableDevices - // (e.g. called by OnEnumerateDevices) and then look up the requested ID when - // a device is opened (see OnOpen). - // Used only on the device thread. + // We cache the enumerated video capture devices in + // GetAvailableDevicesOnDeviceThread() and then later look up the requested ID + // when a device is created in DoStartDeviceOnDeviceThread(). Used only on the + // device thread. media::VideoCaptureDevice::Names video_capture_devices_; DISALLOW_COPY_AND_ASSIGN(VideoCaptureManager); diff --git a/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc index 83c064655ed..34172d78cb4 100644 --- a/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc +++ b/chromium/content/browser/renderer_host/media/video_capture_manager_unittest.cc @@ -10,8 +10,10 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/renderer_host/media/media_stream_provider.h" +#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/common/media/media_stream_options.h" #include "media/video/capture/video_capture_device.h" @@ -40,30 +42,33 @@ class MockMediaStreamProviderListener : public MediaStreamProviderListener { MediaStreamProviderError)); }; // class MockMediaStreamProviderListener -// Needed as an input argument to Start(). -class MockFrameObserver : public media::VideoCaptureDevice::EventHandler { +// Needed as an input argument to StartCaptureForClient(). +class MockFrameObserver : public VideoCaptureControllerEventHandler { public: - virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE { - return NULL; - } - virtual void OnError() OVERRIDE {} + MOCK_METHOD1(OnError, void(const VideoCaptureControllerID& id)); + + virtual void OnBufferCreated(const VideoCaptureControllerID& id, + base::SharedMemoryHandle handle, + int length, int buffer_id) OVERRIDE {}; + virtual void OnBufferReady(const VideoCaptureControllerID& id, + int buffer_id, + base::Time timestamp) OVERRIDE {}; virtual void OnFrameInfo( - const media::VideoCaptureCapability& info) OVERRIDE {} - virtual void OnIncomingCapturedFrame(const uint8* data, - int length, - base::Time timestamp, - int rotation, - bool flip_vert, - bool flip_horiz) OVERRIDE {} - virtual void OnIncomingCapturedVideoFrame( - const scoped_refptr<media::VideoFrame>& frame, - base::Time timestamp) OVERRIDE {} + const VideoCaptureControllerID& id, + const media::VideoCaptureCapability& format) OVERRIDE {}; + virtual void OnFrameInfoChanged(const VideoCaptureControllerID& id, + int width, + int height, + int frame_rate) OVERRIDE {}; + virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE {}; + + void OnGotControllerCallback(VideoCaptureControllerID) {} }; // Test class class VideoCaptureManagerTest : public testing::Test { public: - VideoCaptureManagerTest() {} + VideoCaptureManagerTest() : next_client_id_(1) {} virtual ~VideoCaptureManagerTest() {} protected: @@ -80,6 +85,48 @@ class VideoCaptureManagerTest : public testing::Test { virtual void TearDown() OVERRIDE {} + void OnGotControllerCallback( + VideoCaptureControllerID id, + base::Closure quit_closure, + bool expect_success, + const base::WeakPtr<VideoCaptureController>& controller) { + if (expect_success) { + ASSERT_TRUE(controller); + ASSERT_TRUE(0 == controllers_.count(id)); + controllers_[id] = controller.get(); + } else { + ASSERT_TRUE(NULL == controller); + } + quit_closure.Run(); + } + + VideoCaptureControllerID StartClient(int session_id, bool expect_success) { + media::VideoCaptureParams params; + params.session_id = session_id; + params.width = 320; + params.height = 240; + params.frame_rate = 30; + + VideoCaptureControllerID client_id(next_client_id_++); + base::RunLoop run_loop; + vcm_->StartCaptureForClient( + params, base::kNullProcessHandle, client_id, frame_observer_.get(), + base::Bind(&VideoCaptureManagerTest::OnGotControllerCallback, + base::Unretained(this), client_id, run_loop.QuitClosure(), + expect_success)); + run_loop.Run(); + return client_id; + } + + void StopClient(VideoCaptureControllerID client_id) { + ASSERT_TRUE(1 == controllers_.count(client_id)); + vcm_->StopCaptureForClient(controllers_[client_id], client_id, + frame_observer_.get()); + controllers_.erase(client_id); + } + + int next_client_id_; + std::map<VideoCaptureControllerID, VideoCaptureController*> controllers_; scoped_refptr<VideoCaptureManager> vcm_; scoped_ptr<MockMediaStreamProviderListener> listener_; scoped_ptr<base::MessageLoop> message_loop_; @@ -108,15 +155,9 @@ TEST_F(VideoCaptureManagerTest, CreateAndClose) { message_loop_->RunUntilIdle(); int video_session_id = vcm_->Open(devices.front()); + VideoCaptureControllerID client_id = StartClient(video_session_id, true); - media::VideoCaptureParams capture_params; - capture_params.session_id = video_session_id; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - vcm_->Start(capture_params, frame_observer_.get()); - - vcm_->Stop(video_session_id, base::Closure()); + StopClient(client_id); vcm_->Close(video_session_id); // Wait to check callbacks before removing the listener. @@ -190,9 +231,9 @@ TEST_F(VideoCaptureManagerTest, OpenNotExisting) { InSequence s; EXPECT_CALL(*listener_, DevicesEnumerated(MEDIA_DEVICE_VIDEO_CAPTURE, _)) .Times(1).WillOnce(SaveArg<1>(&devices)); - EXPECT_CALL(*listener_, Error(MEDIA_DEVICE_VIDEO_CAPTURE, - _, kDeviceNotAvailable)) - .Times(1); + EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); + EXPECT_CALL(*frame_observer_, OnError(_)).Times(1); + EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); vcm_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE); @@ -204,11 +245,15 @@ TEST_F(VideoCaptureManagerTest, OpenNotExisting) { std::string device_id("id_doesnt_exist"); StreamDeviceInfo dummy_device(stream_type, device_name, device_id, false); - // This should fail with error code 'kDeviceNotAvailable'. - vcm_->Open(dummy_device); + // This should fail with an error to the controller. + int session_id = vcm_->Open(dummy_device); + VideoCaptureControllerID client_id = StartClient(session_id, true); + message_loop_->RunUntilIdle(); - // Wait to check callbacks before removing the listener. + StopClient(client_id); + vcm_->Close(session_id); message_loop_->RunUntilIdle(); + vcm_->Unregister(); } @@ -218,17 +263,21 @@ TEST_F(VideoCaptureManagerTest, StartUsingId) { EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(1); - media::VideoCaptureParams capture_params; - capture_params.session_id = VideoCaptureManager::kStartOpenSessionId; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - // Start shall trigger the Open callback. - vcm_->Start(capture_params, frame_observer_.get()); + VideoCaptureControllerID client_id = StartClient( + VideoCaptureManager::kStartOpenSessionId, true); // Stop shall trigger the Close callback - vcm_->Stop(VideoCaptureManager::kStartOpenSessionId, base::Closure()); + StopClient(client_id); + + // Wait to check callbacks before removing the listener. + message_loop_->RunUntilIdle(); + vcm_->Unregister(); +} + +// Start a device without calling Open, using a non-magic ID. +TEST_F(VideoCaptureManagerTest, StartInvalidSession) { + StartClient(22, false); // Wait to check callbacks before removing the listener. message_loop_->RunUntilIdle(); @@ -252,17 +301,12 @@ TEST_F(VideoCaptureManagerTest, CloseWithoutStop) { int video_session_id = vcm_->Open(devices.front()); - media::VideoCaptureParams capture_params; - capture_params.session_id = video_session_id; - capture_params.width = 320; - capture_params.height = 240; - capture_params.frame_per_second = 30; - vcm_->Start(capture_params, frame_observer_.get()); + VideoCaptureControllerID client_id = StartClient(video_session_id, true); // Close will stop the running device, an assert will be triggered in // VideoCaptureManager destructor otherwise. vcm_->Close(video_session_id); - vcm_->Stop(video_session_id, base::Closure()); + StopClient(client_id); // Wait to check callbacks before removing the listener message_loop_->RunUntilIdle(); diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc index e27b703081e..91feb4d4873 100644 --- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc +++ b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.cc @@ -56,8 +56,8 @@ #include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" #include "base/callback_forward.h" +#include "base/callback_helpers.h" #include "base/debug/trace_event.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -693,7 +693,7 @@ void RenderVideoFrame(const SkBitmap& input, } // The result is now ready. - failure_handler.Release(); + ignore_result(failure_handler.Release()); done_cb.Run(true); } @@ -1076,7 +1076,7 @@ void WebContentsVideoCaptureDevice::Impl::Allocate( settings.frame_rate = frame_rate; // Note: the value of |settings.color| doesn't matter if we use only the // VideoFrame based methods on |consumer|. - settings.color = media::VideoCaptureCapability::kI420; + settings.color = media::PIXEL_FORMAT_I420; settings.expected_capture_delay = 0; settings.interlaced = false; @@ -1228,7 +1228,7 @@ WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() { } // static -media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create( +media::VideoCaptureDevice1* WebContentsVideoCaptureDevice::Create( const std::string& device_id) { // Parse device_id into render_process_id and render_view_id. int render_process_id = -1; diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h index 94ac0680f6f..0dfe1d6e2f0 100644 --- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h +++ b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device.h @@ -27,7 +27,7 @@ class RenderWidgetHost; // underlying render view to be swapped out (e.g., due to navigation or // crashes/reloads), without any interruption in capturing. class CONTENT_EXPORT WebContentsVideoCaptureDevice - : public media::VideoCaptureDevice { + : public media::VideoCaptureDevice1 { public: // Construct from a |device_id| string of the form: // "virtual-media-stream://render_process_id:render_view_id", where @@ -37,7 +37,7 @@ class CONTENT_EXPORT WebContentsVideoCaptureDevice // WebContentsVideoCaptureDevice is itself deleted. // TODO(miu): Passing a destroy callback suggests needing to revisit the // design philosophy of an asynchronous DeAllocate(). http://crbug.com/158641 - static media::VideoCaptureDevice* Create(const std::string& device_id); + static media::VideoCaptureDevice1* Create(const std::string& device_id); virtual ~WebContentsVideoCaptureDevice(); diff --git a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc index d88d553fa73..3420b09eb7b 100644 --- a/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc +++ b/chromium/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc @@ -310,8 +310,10 @@ class StubConsumer : public media::VideoCaptureDevice::EventHandler { StubConsumer() : error_encountered_(false), wait_color_yuv_(0xcafe1950) { - buffer_pool_ = - new VideoCaptureBufferPool(kTestWidth * kTestHeight * 3 / 2, 2); + buffer_pool_ = new VideoCaptureBufferPool( + media::VideoFrame::AllocationSize(media::VideoFrame::I420, + gfx::Size(kTestWidth, kTestHeight)), + 2); EXPECT_TRUE(buffer_pool_->Allocate()); } virtual ~StubConsumer() {} @@ -405,7 +407,7 @@ class StubConsumer : public media::VideoCaptureDevice::EventHandler { EXPECT_EQ(kTestWidth, info.width); EXPECT_EQ(kTestHeight, info.height); EXPECT_EQ(kTestFramesPerSecond, info.frame_rate); - EXPECT_EQ(media::VideoCaptureCapability::kI420, info.color); + EXPECT_EQ(media::PIXEL_FORMAT_I420, info.color); } private: @@ -491,7 +493,7 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { // Accessors. CaptureTestSourceController* source() { return &controller_; } - media::VideoCaptureDevice* device() { return device_.get(); } + media::VideoCaptureDevice1* device() { return device_.get(); } StubConsumer* consumer() { return &consumer_; } void SimulateDrawEvent() { @@ -530,7 +532,7 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { scoped_ptr<WebContents> web_contents_; // Finally, the WebContentsVideoCaptureDevice under test. - scoped_ptr<media::VideoCaptureDevice> device_; + scoped_ptr<media::VideoCaptureDevice1> device_; TestBrowserThreadBundle thread_bundle_; }; @@ -545,7 +547,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, InvalidInitialWebContentsError) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -562,7 +564,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -591,7 +593,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -617,7 +619,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, StopWithRendererWorkToDo) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -647,7 +649,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, DeviceRestart) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -689,7 +691,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, GoesThroughAllTheMotions) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); @@ -745,13 +747,13 @@ TEST_F(WebContentsVideoCaptureDeviceTest, RejectsInvalidAllocateParams) { 1280, 720, -2, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&media::VideoCaptureDevice::Allocate, + base::Bind(&media::VideoCaptureDevice1::Allocate, base::Unretained(device()), capture_format, consumer())); @@ -763,7 +765,7 @@ TEST_F(WebContentsVideoCaptureDeviceTest, BadFramesGoodFrames) { kTestWidth, kTestHeight, kTestFramesPerSecond, - media::VideoCaptureCapability::kI420, + media::PIXEL_FORMAT_I420, 0, false, media::ConstantResolutionVideoCaptureDevice); diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc index 0230b26eeaa..76c536baf7c 100644 --- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc +++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.cc @@ -36,13 +36,14 @@ bool WebRTCIdentityServiceHost::OnMessageReceived(const IPC::Message& message, } void WebRTCIdentityServiceHost::OnRequestIdentity( + int sequence_number, const GURL& origin, const std::string& identity_name, const std::string& common_name) { if (!cancel_callback_.is_null()) { DLOG(WARNING) << "Request rejected because the previous request has not finished."; - SendErrorMessage(net::ERR_INSUFFICIENT_RESOURCES); + SendErrorMessage(sequence_number, net::ERR_INSUFFICIENT_RESOURCES); return; } @@ -50,7 +51,7 @@ void WebRTCIdentityServiceHost::OnRequestIdentity( ChildProcessSecurityPolicyImpl::GetInstance(); if (!policy->CanAccessCookiesForOrigin(renderer_process_id_, origin)) { DLOG(WARNING) << "Request rejected because origin access is denied."; - SendErrorMessage(net::ERR_ACCESS_DENIED); + SendErrorMessage(sequence_number, net::ERR_ACCESS_DENIED); return; } @@ -59,9 +60,10 @@ void WebRTCIdentityServiceHost::OnRequestIdentity( identity_name, common_name, base::Bind(&WebRTCIdentityServiceHost::OnComplete, - base::Unretained(this))); + base::Unretained(this), + sequence_number)); if (cancel_callback_.is_null()) { - SendErrorMessage(net::ERR_UNEXPECTED); + SendErrorMessage(sequence_number, net::ERR_UNEXPECTED); } } @@ -69,19 +71,22 @@ void WebRTCIdentityServiceHost::OnCancelRequest() { base::ResetAndReturn(&cancel_callback_).Run(); } -void WebRTCIdentityServiceHost::OnComplete(int status, - const std::string& certificate, - const std::string& private_key) { +void WebRTCIdentityServiceHost::OnComplete(int sequence_number, + int status, + const std::string& certificate, + const std::string& private_key) { cancel_callback_.Reset(); if (status == net::OK) { - Send(new WebRTCIdentityHostMsg_IdentityReady(certificate, private_key)); + Send(new WebRTCIdentityHostMsg_IdentityReady( + sequence_number, certificate, private_key)); } else { - SendErrorMessage(status); + SendErrorMessage(sequence_number, status); } } -void WebRTCIdentityServiceHost::SendErrorMessage(int error) { - Send(new WebRTCIdentityHostMsg_RequestFailed(error)); +void WebRTCIdentityServiceHost::SendErrorMessage(int sequence_number, + int error) { + Send(new WebRTCIdentityHostMsg_RequestFailed(sequence_number, error)); } } // namespace content diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h index 3676223fe86..3a5921c5c2d 100644 --- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h +++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host.h @@ -37,19 +37,26 @@ class CONTENT_EXPORT WebRTCIdentityServiceHost : public BrowserMessageFilter { bool* message_was_ok) OVERRIDE; private: + // |sequence_number| is the same as in the OnRequestIdentity call. // See WebRTCIdentityStore for the meaning of the parameters. - void OnComplete(int status, + void OnComplete(int sequence_number, + int status, const std::string& certificate, const std::string& private_key); - // See WebRTCIdentityStore for the meaning of the parameters. - void OnRequestIdentity(const GURL& origin, + // |sequence_number| is a renderer wide unique number for each request and + // will be echoed in the response to handle the possibility that the renderer + // cancels the request after the browser sends the response and before it's + // received by the renderer. + // See WebRTCIdentityStore for the meaning of the other parameters. + void OnRequestIdentity(int sequence_number, + const GURL& origin, const std::string& identity_name, const std::string& common_name); void OnCancelRequest(); - void SendErrorMessage(int error); + void SendErrorMessage(int sequence_number, int error); int renderer_process_id_; base::Closure cancel_callback_; diff --git a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc index 341378d3c7b..cbedf512bc5 100644 --- a/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc +++ b/chromium/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc @@ -24,6 +24,7 @@ static const char FAKE_CERTIFICATE[] = "fake cert"; static const char FAKE_PRIVATE_KEY[] = "fake private key"; static const int FAKE_ERROR = 100; static const int FAKE_RENDERER_ID = 10; +const int FAKE_SEQUENCE_NUMBER = 1; class MockWebRTCIdentityStore : public WebRTCIdentityStore { public: @@ -105,8 +106,10 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test { void SendRequestToHost() { bool ok; host_->OnMessageReceived( - WebRTCIdentityMsg_RequestIdentity( - GURL(FAKE_ORIGIN), FAKE_IDENTITY_NAME, FAKE_COMMON_NAME), + WebRTCIdentityMsg_RequestIdentity(FAKE_SEQUENCE_NUMBER, + GURL(FAKE_ORIGIN), + FAKE_IDENTITY_NAME, + FAKE_COMMON_NAME), &ok); ASSERT_TRUE(ok); } @@ -122,9 +125,10 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test { IPC::Message ipc = host_->GetLastMessage(); EXPECT_EQ(ipc.type(), WebRTCIdentityHostMsg_RequestFailed::ID); - Tuple1<int> error_in_message; + Tuple2<int, int> error_in_message; WebRTCIdentityHostMsg_RequestFailed::Read(&ipc, &error_in_message); - EXPECT_EQ(error, error_in_message.a); + EXPECT_EQ(FAKE_SEQUENCE_NUMBER, error_in_message.a); + EXPECT_EQ(error, error_in_message.b); } void VerifyIdentityReadyMessage(const std::string& cert, @@ -133,10 +137,11 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test { IPC::Message ipc = host_->GetLastMessage(); EXPECT_EQ(ipc.type(), WebRTCIdentityHostMsg_IdentityReady::ID); - Tuple2<std::string, std::string> identity_in_message; + Tuple3<int, std::string, std::string> identity_in_message; WebRTCIdentityHostMsg_IdentityReady::Read(&ipc, &identity_in_message); - EXPECT_EQ(cert, identity_in_message.a); - EXPECT_EQ(key, identity_in_message.b); + EXPECT_EQ(FAKE_SEQUENCE_NUMBER, identity_in_message.a); + EXPECT_EQ(cert, identity_in_message.b); + EXPECT_EQ(key, identity_in_message.c); } protected: diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event.cc index 3cb2af1397b..646b358ac8d 100644 --- a/chromium/content/browser/renderer_host/native_web_keyboard_event.cc +++ b/chromium/content/browser/renderer_host/native_web_keyboard_event.cc @@ -4,7 +4,7 @@ #include "content/public/browser/native_web_keyboard_event.h" -#include "ui/base/events/event_constants.h" +#include "ui/events/event_constants.h" namespace content { diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc index 1658d8d3dc2..ad3aae357cb 100644 --- a/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc +++ b/chromium/content/browser/renderer_host/native_web_keyboard_event_aura.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "content/browser/renderer_host/web_input_event_aura.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" namespace { @@ -77,7 +77,8 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent( nativeKeyCode = character; text[0] = character; unmodifiedText[0] = character; - isSystemKey = (state & ui::EF_ALT_DOWN) != 0; + isSystemKey = + (state & ui::EF_ALT_DOWN) != 0 && (state & ui::EF_ALTGR_DOWN) == 0; setKeyIdentifierFromWindowsKeyCode(); } diff --git a/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc b/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc index f7631945fec..74ce6a36cbb 100644 --- a/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc +++ b/chromium/content/browser/renderer_host/native_web_keyboard_event_gtk.cc @@ -6,9 +6,7 @@ #include <gdk/gdk.h> -#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" - -using WebKit::WebInputEventFactory; +#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h" namespace { @@ -32,7 +30,7 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent() } NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event) - : WebKeyboardEvent(WebInputEventFactory::keyboardEvent(&native_event->key)), + : WebKeyboardEvent(WebKeyboardEventBuilder::Build(&native_event->key)), skip_in_browser(false), match_edit_command(false) { CopyEventTo(native_event, &os_event); @@ -41,9 +39,9 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event) NativeWebKeyboardEvent::NativeWebKeyboardEvent(wchar_t character, int state, double time_stamp_seconds) - : WebKeyboardEvent(WebInputEventFactory::keyboardEvent(character, - state, - time_stamp_seconds)), + : WebKeyboardEvent(WebKeyboardEventBuilder::Build(character, + state, + time_stamp_seconds)), os_event(NULL), skip_in_browser(false), match_edit_command(false) { diff --git a/chromium/content/browser/renderer_host/overscroll_configuration.cc b/chromium/content/browser/renderer_host/overscroll_configuration.cc index 77585e5fb02..f7c5579f56f 100644 --- a/chromium/content/browser/renderer_host/overscroll_configuration.cc +++ b/chromium/content/browser/renderer_host/overscroll_configuration.cc @@ -11,7 +11,8 @@ namespace { float g_horiz_threshold_complete = 0.25f; float g_vert_threshold_complete = 0.20f; -float g_horiz_threshold_start = 50.f; +float g_horiz_threshold_start_touchscreen = 50.f; +float g_horiz_threshold_start_touchpad = 50.f; float g_vert_threshold_start = 0.f; float g_horiz_resist_after = 30.f; @@ -31,8 +32,12 @@ void SetOverscrollConfig(OverscrollConfig config, float value) { g_vert_threshold_complete = value; break; - case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START: - g_horiz_threshold_start = value; + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN: + g_horiz_threshold_start_touchscreen = value; + break; + + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD: + g_horiz_threshold_start_touchpad = value; break; case OVERSCROLL_CONFIG_VERT_THRESHOLD_START: @@ -61,8 +66,11 @@ float GetOverscrollConfig(OverscrollConfig config) { case OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE: return g_vert_threshold_complete; - case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START: - return g_horiz_threshold_start; + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN: + return g_horiz_threshold_start_touchscreen; + + case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD: + return g_horiz_threshold_start_touchpad; case OVERSCROLL_CONFIG_VERT_THRESHOLD_START: return g_vert_threshold_start; diff --git a/chromium/content/browser/renderer_host/overscroll_controller.cc b/chromium/content/browser/renderer_host/overscroll_controller.cc index 3fed4c332a7..f07efab61f4 100644 --- a/chromium/content/browser/renderer_host/overscroll_controller.cc +++ b/chromium/content/browser/renderer_host/overscroll_controller.cc @@ -9,6 +9,8 @@ #include "content/public/browser/overscroll_configuration.h" #include "content/public/browser/render_widget_host_view.h" +using WebKit::WebInputEvent; + namespace content { OverscrollController::OverscrollController( @@ -62,23 +64,25 @@ bool OverscrollController::WillDispatchEvent( // touch-scrolls maintain state in the renderer side (in the compositor, for // example), and the event that completes this action needs to be sent to // the renderer so that those states can be updated/reset appropriately. - // Send the event through the host when appropriate. - if (ShouldForwardToHost(event)) { + if (WebKit::WebInputEvent::isGestureEventType(event.type)) { + // A gesture-event isn't sent to the GestureEventFilter when overscroll is + // in progress. So dispatch the event through the RenderWidgetHost so that + // it can reach the GestureEventFilter. const WebKit::WebGestureEvent& gevent = static_cast<const WebKit::WebGestureEvent&>(event); return render_widget_host_->ShouldForwardGestureEvent( GestureEventWithLatencyInfo(gevent, latency_info)); } - return false; + return true; } if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) { SetOverscrollMode(OVERSCROLL_NONE); - // The overscroll gesture status is being reset. If the event is a - // gesture event (from either touchscreen or trackpad), then make sure the - // host gets the event first (if it didn't already process it). - if (ShouldForwardToHost(event)) { + if (WebKit::WebInputEvent::isGestureEventType(event.type)) { + // A gesture-event isn't sent to the GestureEventFilter when overscroll is + // in progress. So dispatch the event through the RenderWidgetHost so that + // it can reach the GestureEventFilter. const WebKit::WebGestureEvent& gevent = static_cast<const WebKit::WebGestureEvent&>(event); return render_widget_host_->ShouldForwardGestureEvent( @@ -90,16 +94,9 @@ bool OverscrollController::WillDispatchEvent( } if (overscroll_mode_ != OVERSCROLL_NONE) { - // Consume the event and update overscroll state when in the middle of the - // overscroll gesture. - ProcessEventForOverscroll(event); - - if (event.type == WebKit::WebInputEvent::TouchEnd || - event.type == WebKit::WebInputEvent::TouchCancel || - event.type == WebKit::WebInputEvent::TouchMove) { - return true; - } - return false; + // Consume the event only if it updates the overscroll state. + if (ProcessEventForOverscroll(event)) + return false; } return true; @@ -224,24 +221,29 @@ bool OverscrollController::DispatchEventResetsState( } } -void OverscrollController::ProcessEventForOverscroll( +bool OverscrollController::ProcessEventForOverscroll( const WebKit::WebInputEvent& event) { + bool event_processed = false; switch (event.type) { case WebKit::WebInputEvent::MouseWheel: { const WebKit::WebMouseWheelEvent& wheel = static_cast<const WebKit::WebMouseWheelEvent&>(event); if (!wheel.hasPreciseScrollingDeltas) - return; + break; ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX, - wheel.deltaY * wheel.accelerationRatioY); + wheel.deltaY * wheel.accelerationRatioY, + wheel.type); + event_processed = true; break; } case WebKit::WebInputEvent::GestureScrollUpdate: { const WebKit::WebGestureEvent& gesture = static_cast<const WebKit::WebGestureEvent&>(event); ProcessOverscroll(gesture.data.scrollUpdate.deltaX, - gesture.data.scrollUpdate.deltaY); + gesture.data.scrollUpdate.deltaY, + gesture.type); + event_processed = true; break; } case WebKit::WebInputEvent::GestureFlingStart: { @@ -254,12 +256,14 @@ void OverscrollController::ProcessEventForOverscroll( if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) || (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) { CompleteAction(); + event_processed = true; break; } } else if (fabs(velocity_y) > kFlingVelocityThreshold) { if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) || (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) { CompleteAction(); + event_processed = true; break; } } @@ -274,15 +278,20 @@ void OverscrollController::ProcessEventForOverscroll( WebKit::WebInputEvent::isTouchEventType(event.type)) << "Received unexpected event: " << event.type; } + return event_processed; } -void OverscrollController::ProcessOverscroll(float delta_x, float delta_y) { +void OverscrollController::ProcessOverscroll(float delta_x, + float delta_y, + WebKit::WebInputEvent::Type type) { if (scroll_state_ != STATE_CONTENT_SCROLLING) overscroll_delta_x_ += delta_x; overscroll_delta_y_ += delta_y; float horiz_threshold = GetOverscrollConfig( - OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START); + WebInputEvent::isGestureEventType(type) ? + OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN : + OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD); float vert_threshold = GetOverscrollConfig( OVERSCROLL_CONFIG_VERT_THRESHOLD_START); if (fabs(overscroll_delta_x_) <= horiz_threshold && @@ -359,14 +368,4 @@ void OverscrollController::SetOverscrollMode(OverscrollMode mode) { delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_); } -bool OverscrollController::ShouldForwardToHost( - const WebKit::WebInputEvent& event) const { - if (!WebKit::WebInputEvent::isGestureEventType(event.type)) - return false; - - // If the RenderWidgetHost already processed this event, then the event must - // not be sent again. - return !render_widget_host_->HasQueuedGestureEvents(); -} - } // namespace content diff --git a/chromium/content/browser/renderer_host/overscroll_controller.h b/chromium/content/browser/renderer_host/overscroll_controller.h index fc1fc5eb47f..fab531947ee 100644 --- a/chromium/content/browser/renderer_host/overscroll_controller.h +++ b/chromium/content/browser/renderer_host/overscroll_controller.h @@ -89,13 +89,16 @@ class OverscrollController { // overscroll gesture status. bool DispatchEventResetsState(const WebKit::WebInputEvent& event) const; - // Processes an event and updates internal state for overscroll. - void ProcessEventForOverscroll(const WebKit::WebInputEvent& event); + // Processes an event to update the internal state for overscroll. Returns + // true if the state is updated, false otherwise. + bool ProcessEventForOverscroll(const WebKit::WebInputEvent& event); // Processes horizontal overscroll. This can update both the overscroll mode // and the over scroll amount (i.e. |overscroll_mode_|, |overscroll_delta_x_| // and |overscroll_delta_y_|). - void ProcessOverscroll(float delta_x, float delta_y); + void ProcessOverscroll(float delta_x, + float delta_y, + WebKit::WebInputEvent::Type event_type); // Completes the desired action from the current gesture. void CompleteAction(); @@ -104,10 +107,6 @@ class OverscrollController { // appropriate). void SetOverscrollMode(OverscrollMode new_mode); - // Returns whether the input event should be forwarded to the - // RenderWidgetHost. - bool ShouldForwardToHost(const WebKit::WebInputEvent& event) const; - // The RenderWidgetHost that owns this overscroll controller. RenderWidgetHostImpl* render_widget_host_; diff --git a/chromium/content/browser/renderer_host/p2p/OWNERS b/chromium/content/browser/renderer_host/p2p/OWNERS index 17c3a1e6983..f12c2d60e83 100644 --- a/chromium/content/browser/renderer_host/p2p/OWNERS +++ b/chromium/content/browser/renderer_host/p2p/OWNERS @@ -1,2 +1,3 @@ -sergeyu@chromium.org hclam@chromium.org +mallinath@chromium.org +sergeyu@chromium.org diff --git a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc index f1249d51e89..d1a60b451a8 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.cc @@ -51,7 +51,9 @@ class P2PSocketDispatcherHost::DnsRequest { net::HostResolver::RequestInfo info(net::HostPortPair(host_name_, 0)); int result = resolver_.Resolve( - info, &addresses_, + info, + net::DEFAULT_PRIORITY, + &addresses_, base::Bind(&P2PSocketDispatcherHost::DnsRequest::OnDone, base::Unretained(this)), net::BoundNetLog()); @@ -190,8 +192,8 @@ void P2PSocketDispatcherHost::OnCreateSocket( return; } - scoped_ptr<P2PSocketHost> socket( - P2PSocketHost::Create(this, socket_id, type, url_context_.get())); + scoped_ptr<P2PSocketHost> socket(P2PSocketHost::Create( + this, socket_id, type, url_context_.get(), &throttler_)); if (!socket) { Send(new P2PMsg_OnError(socket_id)); diff --git a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h index 8a31ae558b1..2369eb2dbea 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h +++ b/chromium/content/browser/renderer_host/p2p/socket_dispatcher_host.h @@ -7,6 +7,7 @@ #include <map> +#include "content/browser/renderer_host/p2p/socket_host_throttler.h" #include "content/common/p2p_sockets.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_thread.h" @@ -84,6 +85,7 @@ class P2PSocketDispatcherHost bool monitoring_networks_; std::set<DnsRequest*> dns_requests_; + P2PMessageThrottler throttler_; DISALLOW_COPY_AND_ASSIGN(P2PSocketDispatcherHost); }; diff --git a/chromium/content/browser/renderer_host/p2p/socket_host.cc b/chromium/content/browser/renderer_host/p2p/socket_host.cc index 027bf2a608b..4b45e99adad 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host.cc @@ -15,7 +15,8 @@ const uint32 kStunMagicCookie = 0x2112A442; namespace content { -P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender, int id) +P2PSocketHost::P2PSocketHost(IPC::Sender* message_sender, + int id) : message_sender_(message_sender), id_(id), state_(STATE_UNINITIALIZED) { @@ -73,11 +74,11 @@ bool P2PSocketHost::IsRequestOrResponse(StunMessageType type) { // static P2PSocketHost* P2PSocketHost::Create( IPC::Sender* message_sender, int id, P2PSocketType type, - net::URLRequestContextGetter* url_context) { + net::URLRequestContextGetter* url_context, + P2PMessageThrottler* throttler) { switch (type) { case P2P_SOCKET_UDP: - return new P2PSocketHostUdp(message_sender, id); - + return new P2PSocketHostUdp(message_sender, id, throttler); case P2P_SOCKET_TCP_SERVER: return new P2PSocketHostTcpServer( message_sender, id, P2P_SOCKET_TCP_CLIENT); diff --git a/chromium/content/browser/renderer_host/p2p/socket_host.h b/chromium/content/browser/renderer_host/p2p/socket_host.h index 8228300408e..fa186c84b49 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host.h +++ b/chromium/content/browser/renderer_host/p2p/socket_host.h @@ -7,7 +7,6 @@ #include "content/common/content_export.h" #include "content/common/p2p_sockets.h" - #include "net/base/ip_endpoint.h" namespace IPC { @@ -19,6 +18,7 @@ class URLRequestContextGetter; } namespace content { +class P2PMessageThrottler; // Base class for P2P sockets. class CONTENT_EXPORT P2PSocketHost { @@ -27,7 +27,8 @@ class CONTENT_EXPORT P2PSocketHost { // Creates P2PSocketHost of the specific type. static P2PSocketHost* Create(IPC::Sender* message_sender, int id, P2PSocketType type, - net::URLRequestContextGetter* url_context); + net::URLRequestContextGetter* url_context, + P2PMessageThrottler* throttler); virtual ~P2PSocketHost(); @@ -45,6 +46,8 @@ class CONTENT_EXPORT P2PSocketHost { protected: friend class P2PSocketHostTcpTestBase; + // TODO(mallinath) - Remove this below enum and use one defined in + // libjingle/souce/talk/p2p/base/stun.h enum StunMessageType { STUN_BINDING_REQUEST = 0x0001, STUN_BINDING_RESPONSE = 0x0101, @@ -58,7 +61,15 @@ class CONTENT_EXPORT P2PSocketHost { STUN_SEND_REQUEST = 0x0004, STUN_SEND_RESPONSE = 0x0104, STUN_SEND_ERROR_RESPONSE = 0x0114, - STUN_DATA_INDICATION = 0x0115 + STUN_DATA_INDICATION = 0x0115, + TURN_SEND_INDICATION = 0x0016, + TURN_DATA_INDICATION = 0x0017, + TURN_CREATE_PERMISSION_REQUEST = 0x0008, + TURN_CREATE_PERMISSION_RESPONSE = 0x0108, + TURN_CREATE_PERMISSION_ERROR_RESPONSE = 0x0118, + TURN_CHANNEL_BIND_REQUEST = 0x0009, + TURN_CHANNEL_BIND_RESPONSE = 0x0109, + TURN_CHANNEL_BIND_ERROR_RESPONSE = 0x0119, }; enum State { diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc index 8e13bf8f5b5..de9927a7168 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.cc @@ -134,18 +134,23 @@ void P2PSocketHostTcpBase::OnConnected(int result) { if (IsTlsClientSocket(type_)) { state_ = STATE_TLS_CONNECTING; StartTls(); - } else { - if (IsPseudoTlsClientSocket(type_)) { - socket_.reset(new jingle_glue::FakeSSLClientSocket(socket_.release())); + } else if (IsPseudoTlsClientSocket(type_)) { + scoped_ptr<net::StreamSocket> transport_socket = socket_.Pass(); + socket_.reset( + new jingle_glue::FakeSSLClientSocket(transport_socket.Pass())); + state_ = STATE_TLS_CONNECTING; + int status = socket_->Connect( + base::Bind(&P2PSocketHostTcpBase::ProcessTlsSslConnectDone, + base::Unretained(this))); + if (status != net::ERR_IO_PENDING) { + ProcessTlsSslConnectDone(status); } - + } else { // If we are not doing TLS, we are ready to send data now. // In case of TLS, SignalConnect will be sent only after TLS handshake is // successfull. So no buffering will be done at socket handlers if any // packets sent before that by the application. - state_ = STATE_OPEN; - DoSendSocketCreateMsg(); - DoRead(); + OnOpen(); } } @@ -155,7 +160,7 @@ void P2PSocketHostTcpBase::StartTls() { scoped_ptr<net::ClientSocketHandle> socket_handle( new net::ClientSocketHandle()); - socket_handle->set_socket(socket_.release()); + socket_handle->SetSocket(socket_.Pass()); net::SSLClientSocketContext context; context.cert_verifier = url_context_->GetURLRequestContext()->cert_verifier(); @@ -171,24 +176,27 @@ void P2PSocketHostTcpBase::StartTls() { net::ClientSocketFactory::GetDefaultFactory(); DCHECK(socket_factory); - socket_.reset(socket_factory->CreateSSLClientSocket( - socket_handle.release(), dest_host_port_pair, ssl_config, context)); + socket_ = socket_factory->CreateSSLClientSocket( + socket_handle.Pass(), dest_host_port_pair, ssl_config, context); int status = socket_->Connect( - base::Bind(&P2PSocketHostTcpBase::ProcessTlsConnectDone, + base::Bind(&P2PSocketHostTcpBase::ProcessTlsSslConnectDone, base::Unretained(this))); if (status != net::ERR_IO_PENDING) { - ProcessTlsConnectDone(status); + ProcessTlsSslConnectDone(status); } } -void P2PSocketHostTcpBase::ProcessTlsConnectDone(int status) { +void P2PSocketHostTcpBase::ProcessTlsSslConnectDone(int status) { DCHECK_NE(status, net::ERR_IO_PENDING); DCHECK_EQ(state_, STATE_TLS_CONNECTING); if (status != net::OK) { OnError(); return; } + OnOpen(); +} +void P2PSocketHostTcpBase::OnOpen() { state_ = STATE_OPEN; DoSendSocketCreateMsg(); DoRead(); diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h index e5fa08ff93f..c0047a1c26a 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h +++ b/chromium/content/browser/renderer_host/p2p/socket_host_tcp.h @@ -61,7 +61,7 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost { // SSL/TLS connection functions. void StartTls(); - void ProcessTlsConnectDone(int status); + void ProcessTlsSslConnectDone(int status); void DidCompleteRead(int result); void DoRead(); @@ -74,6 +74,8 @@ class CONTENT_EXPORT P2PSocketHostTcpBase : public P2PSocketHost { void OnRead(int result); void OnWritten(int result); + // Helper method to send socket create message and start read. + void OnOpen(); void DoSendSocketCreateMsg(); net::IPEndPoint remote_address_; diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_throttler.cc b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.cc new file mode 100644 index 00000000000..50a4dd0ac83 --- /dev/null +++ b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2013 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/browser/renderer_host/p2p/socket_host_throttler.h" +#include "third_party/libjingle/source/talk/base/ratelimiter.h" +#include "third_party/libjingle/source/talk/base/timing.h" + +namespace content { + +namespace { + +const int kMaxIceMessageBandwidth = 256 * 1024; + +} // namespace + + +P2PMessageThrottler::P2PMessageThrottler() + : timing_(new talk_base::Timing()), + rate_limiter_(new talk_base::RateLimiter(kMaxIceMessageBandwidth, 1.0)) { +} + +P2PMessageThrottler::~P2PMessageThrottler() { +} + +void P2PMessageThrottler::SetTiming(scoped_ptr<talk_base::Timing> timing) { + timing_ = timing.Pass(); +} + +void P2PMessageThrottler::SetSendIceBandwidth(int bandwidth_kbps) { + rate_limiter_.reset(new talk_base::RateLimiter(bandwidth_kbps, 1.0)); +} + +bool P2PMessageThrottler::DropNextPacket(size_t packet_len) { + double now = timing_->TimerNow(); + if (!rate_limiter_->CanUse(packet_len, now)) { + // Exceeding the send rate, this packet should be dropped. + return true; + } + + rate_limiter_->Use(packet_len, now); + return false; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_throttler.h b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.h new file mode 100644 index 00000000000..166d30054f5 --- /dev/null +++ b/chromium/content/browser/renderer_host/p2p/socket_host_throttler.h @@ -0,0 +1,40 @@ +// Copyright (c) 2013 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_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_THROTTLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_THROTTLER_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" + +namespace talk_base { +class RateLimiter; +class Timing; +} + +namespace content { + +// A very simple message throtller. User of this class must drop the packet if +// DropNextPacket returns false for that packet. This method verifies the +// current sendrate against the required sendrate. + +class CONTENT_EXPORT P2PMessageThrottler { + public: + P2PMessageThrottler(); + virtual ~P2PMessageThrottler(); + + void SetTiming(scoped_ptr<talk_base::Timing> timing); + bool DropNextPacket(size_t packet_len); + void SetSendIceBandwidth(int bandwith_kbps); + + private: + scoped_ptr<talk_base::Timing> timing_; + scoped_ptr<talk_base::RateLimiter> rate_limiter_; + + DISALLOW_COPY_AND_ASSIGN(P2PMessageThrottler); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_P2P_SOCKET_HOST_THROTTLER_H_ diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc index bcfb282a2c4..54c2b4cd4ed 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/debug/trace_event.h" +#include "content/browser/renderer_host/p2p/socket_host_throttler.h" #include "content/common/p2p_messages.h" #include "ipc/ipc_sender.h" #include "net/base/io_buffer.h" @@ -61,11 +62,14 @@ P2PSocketHostUdp::PendingPacket::PendingPacket( P2PSocketHostUdp::PendingPacket::~PendingPacket() { } -P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender, int id) +P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender, + int id, + P2PMessageThrottler* throttler) : P2PSocketHost(message_sender, id), socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())), send_pending_(false), - send_packet_count_(0) { + send_packet_count_(0), + throttler_(throttler) { } P2PSocketHostUdp::~P2PSocketHostUdp() { @@ -180,6 +184,12 @@ void P2PSocketHostUdp::Send(const net::IPEndPoint& to, OnError(); return; } + + if (throttler_->DropNextPacket(data.size())) { + LOG(INFO) << "STUN message is dropped due to high volume."; + // Do not reset socket. + return; + } } if (send_pending_) { diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp.h b/chromium/content/browser/renderer_host/p2p/socket_host_udp.h index e9eb36393f4..befac2cae12 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_udp.h +++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp.h @@ -21,9 +21,12 @@ namespace content { +class P2PMessageThrottler; + class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost { public: - P2PSocketHostUdp(IPC::Sender* message_sender, int id); + P2PSocketHostUdp(IPC::Sender* message_sender, int id, + P2PMessageThrottler* throttler); virtual ~P2PSocketHostUdp(); // P2PSocketHost overrides. @@ -71,6 +74,7 @@ class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost { // Set of peer for which we have received STUN binding request or // response or relay allocation request or response. ConnectedPeerSet connected_peers_; + P2PMessageThrottler* throttler_; DISALLOW_COPY_AND_ASSIGN(P2PSocketHostUdp); }; diff --git a/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc b/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc index 9ecd56abd54..d42efbe3c35 100644 --- a/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc +++ b/chromium/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc @@ -10,12 +10,14 @@ #include "base/logging.h" #include "base/sys_byteorder.h" #include "content/browser/renderer_host/p2p/socket_host_test_utils.h" +#include "content/browser/renderer_host/p2p/socket_host_throttler.h" #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/udp/datagram_server_socket.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/base/timing.h" using ::testing::_; using ::testing::DeleteArg; @@ -24,6 +26,16 @@ using ::testing::Return; namespace { +class FakeTiming : public talk_base::Timing { + public: + FakeTiming() : now_(0.0) {} + virtual double TimerNow() OVERRIDE { return now_; } + void set_now(double now) { now_ = now; } + + private: + double now_; +}; + class FakeDatagramServerSocket : public net::DatagramServerSocket { public: typedef std::pair<net::IPEndPoint, std::vector<char> > UDPPacket; @@ -161,7 +173,7 @@ class P2PSocketHostUdpTest : public testing::Test { MatchMessage(static_cast<uint32>(P2PMsg_OnSocketCreated::ID)))) .WillOnce(DoAll(DeleteArg<0>(), Return(true))); - socket_host_.reset(new P2PSocketHostUdp(&sender_, 0)); + socket_host_.reset(new P2PSocketHostUdp(&sender_, 0, &throttler_)); socket_ = new FakeDatagramServerSocket(&sent_packets_); socket_host_->socket_.reset(socket_); @@ -170,8 +182,12 @@ class P2PSocketHostUdpTest : public testing::Test { dest1_ = ParseAddress(kTestIpAddress1, kTestPort1); dest2_ = ParseAddress(kTestIpAddress2, kTestPort2); + + scoped_ptr<talk_base::Timing> timing(new FakeTiming()); + throttler_.SetTiming(timing.Pass()); } + P2PMessageThrottler throttler_; std::deque<FakeDatagramServerSocket::UDPPacket> sent_packets_; FakeDatagramServerSocket* socket_; // Owned by |socket_host_|. scoped_ptr<P2PSocketHostUdp> socket_host_; @@ -289,4 +305,62 @@ TEST_F(P2PSocketHostUdpTest, SendAfterStunResponseDifferentHost) { socket_host_->Send(dest2_, packet); } +// Verify throttler not allowing unlimited sending of ICE messages to +// any destination. +TEST_F(P2PSocketHostUdpTest, ThrottleAfterLimit) { + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(2) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet1; + CreateStunRequest(&packet1); + throttler_.SetSendIceBandwidth(packet1.size() * 2); + socket_host_->Send(dest1_, packet1); + socket_host_->Send(dest2_, packet1); + + net::IPEndPoint dest3 = ParseAddress(kTestIpAddress1, 2222); + // This packet must be dropped by the throttler. + socket_host_->Send(dest3, packet1); + ASSERT_EQ(sent_packets_.size(), 2U); +} + +// Verify we can send packets to a known destination when ICE throttling is +// active. +TEST_F(P2PSocketHostUdpTest, ThrottleAfterLimitAfterReceive) { + // Receive packet from |dest1_|. + std::vector<char> request_packet; + CreateStunRequest(&request_packet); + + EXPECT_CALL(sender_, Send(MatchPacketMessage(request_packet))) + .WillOnce(DoAll(DeleteArg<0>(), Return(true))); + socket_->ReceivePacket(dest1_, request_packet); + + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(4) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + + std::vector<char> packet1; + CreateStunRequest(&packet1); + throttler_.SetSendIceBandwidth(packet1.size()); + // |dest1_| is known address, throttling will not be applied. + socket_host_->Send(dest1_, packet1); + // Trying to send the packet to dest1_ in the same window. It should go. + socket_host_->Send(dest1_, packet1); + + // Throttler should allow this packet to go through. + socket_host_->Send(dest2_, packet1); + + net::IPEndPoint dest3 = ParseAddress(kTestIpAddress1, 2223); + // This packet will be dropped, as limit only for a single packet. + socket_host_->Send(dest3, packet1); + net::IPEndPoint dest4 = ParseAddress(kTestIpAddress1, 2224); + // This packet should also be dropped. + socket_host_->Send(dest4, packet1); + // |dest1| is known, we can send as many packets to it. + socket_host_->Send(dest1_, packet1); + ASSERT_EQ(sent_packets_.size(), 4U); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/OWNERS b/chromium/content/browser/renderer_host/pepper/OWNERS index dc19a03cb21..33d4c55751a 100644 --- a/chromium/content/browser/renderer_host/pepper/OWNERS +++ b/chromium/content/browser/renderer_host/pepper/OWNERS @@ -1,3 +1,4 @@ +bbudge@chromium.org dmichael@chromium.org raymes@chromium.org yzshen@chromium.org diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc index 10eef8b5515..db3f8eff2ae 100644 --- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc +++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc @@ -4,6 +4,7 @@ #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/pepper_message_filter.h" #include "content/browser/tracing/trace_message_filter.h" #include "content/common/pepper_renderer_instance_data.h" #include "content/public/browser/render_view_host.h" @@ -18,23 +19,17 @@ BrowserPpapiHost* BrowserPpapiHost::CreateExternalPluginProcess( ppapi::PpapiPermissions permissions, base::ProcessHandle plugin_child_process, IPC::ChannelProxy* channel, - net::HostResolver* host_resolver, int render_process_id, int render_view_id, const base::FilePath& profile_directory) { - scoped_refptr<PepperMessageFilter> pepper_message_filter( - new PepperMessageFilter(permissions, - host_resolver, - render_process_id, - render_view_id)); - // The plugin name and path shouldn't be needed for external plugins. BrowserPpapiHostImpl* browser_ppapi_host = new BrowserPpapiHostImpl(sender, permissions, std::string(), - base::FilePath(), profile_directory, true, - pepper_message_filter); + base::FilePath(), profile_directory, true); browser_ppapi_host->set_plugin_process_handle(plugin_child_process); + scoped_refptr<PepperMessageFilter> pepper_message_filter( + new PepperMessageFilter()); channel->AddFilter(pepper_message_filter); channel->AddFilter(browser_ppapi_host->message_filter().get()); channel->AddFilter(new TraceMessageFilter()); @@ -48,17 +43,17 @@ BrowserPpapiHostImpl::BrowserPpapiHostImpl( const std::string& plugin_name, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, - bool external_plugin, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter) + bool external_plugin) : ppapi_host_(new ppapi::host::PpapiHost(sender, permissions)), plugin_process_handle_(base::kNullProcessHandle), plugin_name_(plugin_name), plugin_path_(plugin_path), profile_data_directory_(profile_data_directory), - external_plugin_(external_plugin) { + external_plugin_(external_plugin), + ssl_context_helper_(new SSLContextHelper()) { message_filter_ = new HostMessageFilter(ppapi_host_.get()); ppapi_host_->AddHostFactoryFilter(scoped_ptr<ppapi::host::HostFactory>( - new ContentBrowserPepperHostFactory(this, pepper_message_filter))); + new ContentBrowserPepperHostFactory(this))); } BrowserPpapiHostImpl::~BrowserPpapiHostImpl() { diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h index c46ab13b220..f16d1d82c18 100644 --- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h +++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h @@ -13,7 +13,7 @@ #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" -#include "content/browser/renderer_host/pepper/pepper_message_filter.h" +#include "content/browser/renderer_host/pepper/ssl_context_helper.h" #include "content/common/content_export.h" #include "content/common/pepper_renderer_instance_data.h" #include "content/public/browser/browser_ppapi_host.h" @@ -36,10 +36,7 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost { const std::string& plugin_name, const base::FilePath& plugin_path, const base::FilePath& profile_data_directory, - bool external_plugin, - // TODO (ygorshenin@): remove this once TCP sockets are - // converted to the new design. - const scoped_refptr<PepperMessageFilter>& pepper_message_filter); + bool external_plugin); virtual ~BrowserPpapiHostImpl(); // BrowserPpapiHost. @@ -72,6 +69,10 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost { return message_filter_; } + const scoped_refptr<SSLContextHelper>& ssl_context_helper() const { + return ssl_context_helper_; + } + private: friend class BrowserPpapiHostTest; @@ -103,6 +104,8 @@ class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost { // BrowserPpapiHost::CreateExternalPluginProcess. bool external_plugin_; + scoped_refptr<SSLContextHelper> ssl_context_helper_; + // Tracks all PP_Instances in this plugin and associated renderer-related // data. typedef std::map<PP_Instance, PepperRendererInstanceData> InstanceMap; diff --git a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc index a8c257abacd..c338a990f59 100644 --- a/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc +++ b/chromium/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc @@ -16,8 +16,7 @@ BrowserPpapiHostTest::BrowserPpapiHostTest() std::string(), base::FilePath(), base::FilePath(), - false, - NULL)); + false)); ppapi_host_->set_plugin_process_handle(base::GetCurrentProcessHandle()); } diff --git a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc index f0cefc40396..39e778dfe6b 100644 --- a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc +++ b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc @@ -11,10 +11,12 @@ #include "content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h" #include "content/browser/renderer_host/pepper/pepper_gamepad_host.h" #include "content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.h" +#include "content/browser/renderer_host/pepper/pepper_network_monitor_host.h" #include "content/browser/renderer_host/pepper/pepper_network_proxy_host.h" #include "content/browser/renderer_host/pepper/pepper_print_settings_manager.h" #include "content/browser/renderer_host/pepper/pepper_printing_host.h" #include "content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h" +#include "content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h" #include "content/browser/renderer_host/pepper/pepper_truetype_font_list_host.h" #include "content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h" #include "ppapi/host/message_filter_host.h" @@ -36,18 +38,17 @@ const size_t kMaxSocketsAllowed = 1024; bool CanCreateSocket() { return - PepperUDPSocketMessageFilter::GetNumInstances() + - PepperTCPServerSocketMessageFilter::GetNumInstances() < + PepperTCPServerSocketMessageFilter::GetNumInstances() + + PepperTCPSocketMessageFilter::GetNumInstances() + + PepperUDPSocketMessageFilter::GetNumInstances() < kMaxSocketsAllowed; } } // namespace ContentBrowserPepperHostFactory::ContentBrowserPepperHostFactory( - BrowserPpapiHostImpl* host, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter) - : host_(host), - pepper_message_filter_(pepper_message_filter) { + BrowserPpapiHostImpl* host) + : host_(host) { } ContentBrowserPepperHostFactory::~ContentBrowserPepperHostFactory() { @@ -102,6 +103,15 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(new PepperFileRefHost( host_, instance, params.pp_resource(), file_system, internal_path)); } + case PpapiHostMsg_TCPSocket_Create::ID: { + ppapi::TCPSocketVersion version; + if (!UnpackMessage<PpapiHostMsg_TCPSocket_Create>(message, &version) || + version == ppapi::TCP_SOCKET_VERSION_PRIVATE) { + return scoped_ptr<ResourceHost>(); + } + + return CreateNewTCPSocket(instance, params.pp_resource(), version); + } case PpapiHostMsg_UDPSocket_Create::ID: { if (CanCreateSocket()) { scoped_refptr<ResourceMessageFilter> udp_socket( @@ -155,8 +165,7 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( if (message.type() == PpapiHostMsg_TCPServerSocket_CreatePrivate::ID) { if (CanCreateSocket()) { scoped_refptr<ResourceMessageFilter> tcp_server_socket( - new PepperTCPServerSocketMessageFilter(host_, instance, true, - pepper_message_filter_)); + new PepperTCPServerSocketMessageFilter(this, host_, instance, true)); return scoped_ptr<ResourceHost>(new MessageFilterHost( host_->GetPpapiHost(), instance, params.pp_resource(), tcp_server_socket)); @@ -164,6 +173,10 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(); } } + if (message.type() == PpapiHostMsg_TCPSocket_CreatePrivate::ID) { + return CreateNewTCPSocket(instance, params.pp_resource(), + ppapi::TCP_SOCKET_VERSION_PRIVATE); + } if (message.type() == PpapiHostMsg_UDPSocket_CreatePrivate::ID) { if (CanCreateSocket()) { scoped_refptr<ResourceMessageFilter> udp_socket( @@ -174,6 +187,10 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(); } } + if (message.type() == PpapiHostMsg_NetworkMonitor_Create::ID) { + return scoped_ptr<ResourceHost>( + new PepperNetworkMonitorHost(host_, instance, params.pp_resource())); + } // Flash interfaces. if (GetPermissions().HasPermission(ppapi::PERMISSION_FLASH)) { @@ -191,6 +208,37 @@ scoped_ptr<ResourceHost> ContentBrowserPepperHostFactory::CreateResourceHost( return scoped_ptr<ResourceHost>(); } +scoped_ptr<ppapi::host::ResourceHost> +ContentBrowserPepperHostFactory::CreateAcceptedTCPSocket( + PP_Instance instance, + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket) { + if (!CanCreateSocket()) + return scoped_ptr<ResourceHost>(); + scoped_refptr<ResourceMessageFilter> tcp_socket( + new PepperTCPSocketMessageFilter(host_, instance, version, + socket.Pass())); + return scoped_ptr<ResourceHost>(new MessageFilterHost( + host_->GetPpapiHost(), instance, 0, tcp_socket)); +} + +scoped_ptr<ppapi::host::ResourceHost> +ContentBrowserPepperHostFactory::CreateNewTCPSocket( + PP_Instance instance, + PP_Resource resource, + ppapi::TCPSocketVersion version) { + if (!CanCreateSocket()) + return scoped_ptr<ResourceHost>(); + + scoped_refptr<ResourceMessageFilter> tcp_socket( + new PepperTCPSocketMessageFilter(this, host_, instance, version)); + if (!tcp_socket) + return scoped_ptr<ResourceHost>(); + + return scoped_ptr<ResourceHost>(new MessageFilterHost( + host_->GetPpapiHost(), instance, resource, tcp_socket)); +} + const ppapi::PpapiPermissions& ContentBrowserPepperHostFactory::GetPermissions() const { return host_->GetPpapiHost()->permissions(); diff --git a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h index e39ec35d858..04267a32e8a 100644 --- a/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h +++ b/chromium/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h @@ -7,8 +7,11 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" -#include "content/browser/renderer_host/pepper/pepper_message_filter.h" +#include "base/memory/scoped_ptr.h" +#include "net/socket/tcp_socket.h" +#include "ppapi/c/pp_resource.h" #include "ppapi/host/host_factory.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" namespace ppapi { class PpapiPermissions; @@ -21,11 +24,8 @@ class BrowserPpapiHostImpl; class ContentBrowserPepperHostFactory : public ppapi::host::HostFactory { public: // Non-owning pointer to the filter must outlive this class. - ContentBrowserPepperHostFactory( - BrowserPpapiHostImpl* host, - // TODO (ygorshenin@): remove this once TCP sockets are - // converted to the new design. - const scoped_refptr<PepperMessageFilter>& pepper_message_filter); + explicit ContentBrowserPepperHostFactory(BrowserPpapiHostImpl* host); + virtual ~ContentBrowserPepperHostFactory(); virtual scoped_ptr<ppapi::host::ResourceHost> CreateResourceHost( @@ -34,14 +34,24 @@ class ContentBrowserPepperHostFactory : public ppapi::host::HostFactory { PP_Instance instance, const IPC::Message& message) OVERRIDE; + // Creates ResourceHost for already accepted TCP |socket|. In the case of + // failure returns wrapped NULL. + scoped_ptr<ppapi::host::ResourceHost> CreateAcceptedTCPSocket( + PP_Instance instance, + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket); + private: + scoped_ptr<ppapi::host::ResourceHost> CreateNewTCPSocket( + PP_Instance instance, + PP_Resource resource, + ppapi::TCPSocketVersion version); + const ppapi::PpapiPermissions& GetPermissions() const; // Non-owning pointer. BrowserPpapiHostImpl* host_; - scoped_refptr<PepperMessageFilter> pepper_message_filter_; - DISALLOW_COPY_AND_ASSIGN(ContentBrowserPepperHostFactory); }; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc index 27ba37dfcca..096b908266f 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.cc @@ -90,21 +90,16 @@ int32_t PepperExternalFileRefBackend::GetAbsolutePath( ppapi::host::ReplyMessageContext reply_context) { host_->SendReply(reply_context, PpapiPluginMsg_FileRef_GetAbsolutePathReply(path_.AsUTF8Unsafe())); - return PP_OK; + + // Use PP_OK_COMPLETIONPENDING instead of PP_OK since we've already sent our + // reply above. + return PP_OK_COMPLETIONPENDING; } fileapi::FileSystemURL PepperExternalFileRefBackend::GetFileSystemURL() const { return fileapi::FileSystemURL(); } -std::string PepperExternalFileRefBackend::GetFileSystemURLSpec() const { - return std::string(); -} - -base::FilePath PepperExternalFileRefBackend::GetExternalPath() const { - return path_; -} - int32_t PepperExternalFileRefBackend::CanRead() const { if (!ChildProcessSecurityPolicyImpl::GetInstance()-> CanReadFile(render_process_id_, path_)) { diff --git a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h index 994e9c291cd..cbfaf6ed511 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h @@ -41,8 +41,6 @@ class PepperExternalFileRefBackend : public PepperFileRefBackend { virtual int32_t GetAbsolutePath(ppapi::host::ReplyMessageContext context) OVERRIDE; virtual fileapi::FileSystemURL GetFileSystemURL() const OVERRIDE; - virtual std::string GetFileSystemURLSpec() const OVERRIDE; - virtual base::FilePath GetExternalPath() const OVERRIDE; virtual int32_t CanRead() const OVERRIDE; virtual int32_t CanWrite() const OVERRIDE; @@ -68,6 +66,8 @@ class PepperExternalFileRefBackend : public PepperFileRefBackend { scoped_refptr<base::TaskRunner> task_runner_; base::WeakPtrFactory<PepperExternalFileRefBackend> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperExternalFileRefBackend); }; } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc index 5370af9d853..c38f8826534 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.cc @@ -62,9 +62,9 @@ PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host, } fs_type_ = fs_host->GetType(); - // TODO(teravest): Add support for isolated filesystems. if ((fs_type_ != PP_FILESYSTEMTYPE_LOCALPERSISTENT) && - (fs_type_ != PP_FILESYSTEMTYPE_LOCALTEMPORARY)) { + (fs_type_ != PP_FILESYSTEMTYPE_LOCALTEMPORARY) && + (fs_type_ != PP_FILESYSTEMTYPE_ISOLATED)) { DLOG(ERROR) << "Unsupported filesystem type: " << fs_type_; return; } @@ -116,18 +116,6 @@ fileapi::FileSystemURL PepperFileRefHost::GetFileSystemURL() const { return fileapi::FileSystemURL(); } -std::string PepperFileRefHost::GetFileSystemURLSpec() const { - if (backend_) - return backend_->GetFileSystemURLSpec(); - return std::string(); -} - -base::FilePath PepperFileRefHost::GetExternalPath() const { - if (backend_) - return backend_->GetExternalPath(); - return base::FilePath(); -} - int32_t PepperFileRefHost::CanRead() const { if (backend_) return backend_->CanRead(); diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h index 5d765764326..b97ff331ac0 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_ref_host.h @@ -42,8 +42,6 @@ class PepperFileRefBackend { virtual int32_t GetAbsolutePath( ppapi::host::ReplyMessageContext context) = 0; virtual fileapi::FileSystemURL GetFileSystemURL() const = 0; - virtual std::string GetFileSystemURLSpec() const = 0; - virtual base::FilePath GetExternalPath() const = 0; // Returns an error from the pp_errors.h enum. virtual int32_t CanRead() const = 0; @@ -79,10 +77,6 @@ class CONTENT_EXPORT PepperFileRefHost PP_FileSystemType GetFileSystemType() const; fileapi::FileSystemURL GetFileSystemURL() const; - // Required to support FileIO. - std::string GetFileSystemURLSpec() const; - base::FilePath GetExternalPath() const; - int32_t CanRead() const; int32_t CanWrite() const; int32_t CanCreate() const; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc index 1a10da77b83..fce371d8f99 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc @@ -58,16 +58,17 @@ PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host, PP_FileSystemType type) : ResourceHost(host->GetPpapiHost(), instance, resource), browser_ppapi_host_(host), - weak_factory_(this), type_(type), opened_(false), fs_context_(NULL), - called_open_(false) { + called_open_(false), + weak_factory_(this) { } PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() { - if (fs_context_.get()) - fs_context_->operation_runner()->Shutdown(); + // TODO(teravest): Create a FileSystemOperationRunner + // per-PepperFileSystemBrowserHost, force users of this FileSystem to use it, + // and call Shutdown() on it here. } int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived( @@ -151,6 +152,18 @@ void PepperFileSystemBrowserHost::GotFileSystemContext( fs_context_ = fs_context; } +void PepperFileSystemBrowserHost::GotIsolatedFileSystemContext( + ppapi::host::ReplyMessageContext reply_context, + scoped_refptr<fileapi::FileSystemContext> fs_context) { + fs_context_ = fs_context; + if (fs_context.get()) + reply_context.params.set_result(PP_OK); + else + reply_context.params.set_result(PP_ERROR_FAILED); + host()->SendReply(reply_context, + PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply()); +} + void PepperFileSystemBrowserHost::OpenFileSystemComplete( ppapi::host::ReplyMessageContext reply_context, base::PlatformFileError error, @@ -177,7 +190,22 @@ int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem( root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString( url.GetOrigin(), fsid, "crxfs")); opened_ = true; - return PP_OK; + + int render_process_id = 0; + int unused; + if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(), + &render_process_id, + &unused)) { + return PP_ERROR_FAILED; + } + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::UI, + FROM_HERE, + base::Bind(&GetFileSystemContextFromRenderId, render_process_id), + base::Bind(&PepperFileSystemBrowserHost::GotIsolatedFileSystemContext, + weak_factory_.GetWeakPtr(), + context->MakeReplyMessageContext())); + return PP_OK_COMPLETIONPENDING; } } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h index 3eb146e5b7e..ed648fb9938 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h @@ -47,6 +47,9 @@ class PepperFileSystemBrowserHost : ppapi::host::ReplyMessageContext reply_context, fileapi::FileSystemType file_system_type, scoped_refptr<fileapi::FileSystemContext> fs_context); + void GotIsolatedFileSystemContext( + ppapi::host::ReplyMessageContext reply_context, + scoped_refptr<fileapi::FileSystemContext> fs_context); void OpenFileSystemComplete( ppapi::host::ReplyMessageContext reply_context, base::PlatformFileError error, @@ -60,7 +63,6 @@ class PepperFileSystemBrowserHost : const std::string& fsid); BrowserPpapiHost* browser_ppapi_host_; - base::WeakPtrFactory<PepperFileSystemBrowserHost> weak_factory_; PP_FileSystemType type_; bool opened_; // whether open is successful. @@ -68,6 +70,8 @@ class PepperFileSystemBrowserHost : scoped_refptr<fileapi::FileSystemContext> fs_context_; bool called_open_; // whether open has been called. + base::WeakPtrFactory<PepperFileSystemBrowserHost> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(PepperFileSystemBrowserHost); }; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc index e0e17b367ed..bdbdf871e27 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_host_resolver_message_filter.cc @@ -132,7 +132,7 @@ int32_t PepperHostResolverMessageFilter::OnMsgResolve( if (!render_view_host || !pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, + &request, render_view_host)) { return PP_ERROR_NOACCESS; } @@ -179,6 +179,7 @@ void PepperHostResolverMessageFilter::DoResolve( new PepperLookupRequest<ReplyMessageContext>( host_resolver, request_info, + net::DEFAULT_PRIORITY, bound_info.release(), base::Bind(&PepperHostResolverMessageFilter::OnLookupFinished, this)); lookup_request->Start(); diff --git a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc index 337807d34e1..3b8c801ec1a 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc @@ -63,27 +63,16 @@ fileapi::FileSystemURL PepperInternalFileRefBackend::GetFileSystemURL() const { if (!fs_url_.is_valid() && fs_host_.get() && fs_host_->IsOpened()) { GURL fs_path = fs_host_->GetRootUrl().Resolve( net::EscapePath(path_.substr(1))); - fs_url_ = GetFileSystemContext()->CrackURL(fs_path); + scoped_refptr<fileapi::FileSystemContext> fs_context = + GetFileSystemContext(); + if (fs_context.get()) + fs_url_ = fs_context->CrackURL(fs_path); } return fs_url_; } -std::string PepperInternalFileRefBackend::GetFileSystemURLSpec() const { - if (fs_host_.get() && fs_host_->IsOpened() && - fs_host_->GetRootUrl().is_valid()) { - return fs_host_->GetRootUrl().Resolve( - net::EscapePath(path_.substr(1))).spec(); - } - return std::string(); -} - -base::FilePath PepperInternalFileRefBackend::GetExternalPath() const { - return base::FilePath(); -} - scoped_refptr<fileapi::FileSystemContext> PepperInternalFileRefBackend::GetFileSystemContext() const { - // TODO(teravest): Make this work for CRX file systems. if (!fs_host_.get()) return NULL; return fs_host_->GetFileSystemContext(); @@ -221,7 +210,7 @@ void PepperInternalFileRefBackend::ReadDirectoryComplete( context.params.set_result(ppapi::PlatformFileErrorToPepperError(error)); - std::vector<ppapi::FileRef_CreateInfo> infos; + std::vector<ppapi::FileRefCreateInfo> infos; std::vector<PP_FileType> file_types; if (error == base::PLATFORM_FILE_OK && fs_host_.get()) { std::string dir_path = path_; @@ -235,7 +224,7 @@ void PepperInternalFileRefBackend::ReadDirectoryComplete( else file_types.push_back(PP_FILETYPE_REGULAR); - ppapi::FileRef_CreateInfo info; + ppapi::FileRefCreateInfo info; info.file_system_type = fs_type_; info.file_system_plugin_resource = fs_host_->pp_resource(); std::string path = diff --git a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h index fb8a6cf7603..81d6ab624cd 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h @@ -46,8 +46,6 @@ class PepperInternalFileRefBackend : public PepperFileRefBackend { OVERRIDE; virtual fileapi::FileSystemURL GetFileSystemURL() const OVERRIDE; - virtual std::string GetFileSystemURLSpec() const OVERRIDE; - virtual base::FilePath GetExternalPath() const OVERRIDE; virtual int32_t CanRead() const OVERRIDE; virtual int32_t CanWrite() const OVERRIDE; @@ -82,6 +80,8 @@ class PepperInternalFileRefBackend : public PepperFileRefBackend { mutable fileapi::FileSystemURL fs_url_; base::WeakPtrFactory<PepperInternalFileRefBackend> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperInternalFileRefBackend); }; } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h b/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h index bcb9a60203f..da116fd640c 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_lookup_request.h @@ -24,20 +24,23 @@ class PepperLookupRequest { // callback, when lookup will finish. PepperLookupRequest(net::HostResolver* resolver, const net::HostResolver::RequestInfo& request_info, + net::RequestPriority priority, T* bound_info, const LookupRequestCallback& callback) : resolver_(resolver), request_info_(request_info), + priority_(priority), bound_info_(bound_info), - callback_(callback) { - } + callback_(callback) {} void Start() { - int result = resolver_.Resolve( - request_info_, &addresses_, - base::Bind(&PepperLookupRequest<T>::OnLookupFinished, - base::Unretained(this)), - net::BoundNetLog()); + int result = + resolver_.Resolve(request_info_, + priority_, + &addresses_, + base::Bind(&PepperLookupRequest<T>::OnLookupFinished, + base::Unretained(this)), + net::BoundNetLog()); if (result != net::ERR_IO_PENDING) OnLookupFinished(result); } @@ -50,6 +53,7 @@ class PepperLookupRequest { net::SingleRequestHostResolver resolver_; net::HostResolver::RequestInfo request_info_; + net::RequestPriority priority_; scoped_ptr<T> bound_info_; LookupRequestCallback callback_; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc index 717de350dc7..6434729b734 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.cc @@ -4,118 +4,20 @@ #include "content/browser/renderer_host/pepper/pepper_message_filter.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/files/file_path.h" #include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/threading/sequenced_worker_pool.h" -#include "base/threading/worker_pool.h" -#include "build/build_config.h" #include "content/browser/renderer_host/pepper/pepper_socket_utils.h" -#include "content/browser/renderer_host/pepper/pepper_tcp_socket.h" -#include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/browser/renderer_host/render_view_host_impl.h" -#include "content/common/pepper_messages.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/browser/resource_context.h" #include "content/public/common/content_client.h" -#include "net/base/address_family.h" -#include "net/base/address_list.h" -#include "net/base/host_port_pair.h" -#include "net/base/sys_addrinfo.h" -#include "net/cert/cert_verifier.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/proxy/ppapi_messages.h" -#include "ppapi/shared_impl/api_id.h" -#include "ppapi/shared_impl/private/net_address_private_impl.h" -#include "ppapi/shared_impl/socket_option_data.h" - -using ppapi::NetAddressPrivateImpl; namespace content { -namespace { - -const size_t kMaxSocketsAllowed = 1024; -const uint32 kInvalidSocketID = 0; - -} // namespace -PepperMessageFilter::PepperMessageFilter(int process_id, - BrowserContext* browser_context) - : plugin_type_(PLUGIN_TYPE_IN_PROCESS), - permissions_(), - process_id_(process_id), - external_plugin_render_view_id_(0), - resource_context_(browser_context->GetResourceContext()), - host_resolver_(NULL), - next_socket_id_(1) { - DCHECK(browser_context); - // Keep BrowserContext data in FILE-thread friendly storage. - browser_path_ = browser_context->GetPath(); - incognito_ = browser_context->IsOffTheRecord(); - DCHECK(resource_context_); -} - -PepperMessageFilter::PepperMessageFilter( - const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver) - : plugin_type_(PLUGIN_TYPE_OUT_OF_PROCESS), - permissions_(permissions), - process_id_(0), - external_plugin_render_view_id_(0), - resource_context_(NULL), - host_resolver_(host_resolver), - next_socket_id_(1), - incognito_(false) { - DCHECK(host_resolver); -} - -PepperMessageFilter::PepperMessageFilter( - const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver, - int process_id, - int render_view_id) - : plugin_type_(PLUGIN_TYPE_EXTERNAL_PLUGIN), - permissions_(permissions), - process_id_(process_id), - external_plugin_render_view_id_(render_view_id), - resource_context_(NULL), - host_resolver_(host_resolver), - next_socket_id_(1) { - DCHECK(host_resolver); -} +PepperMessageFilter::PepperMessageFilter() {} +PepperMessageFilter::~PepperMessageFilter() {} bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg, bool* message_was_ok) { bool handled = true; IPC_BEGIN_MESSAGE_MAP_EX(PepperMessageFilter, msg, *message_was_ok) - // TCP messages. - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Create, OnTCPCreate) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_CreatePrivate, - OnTCPCreatePrivate) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Connect, OnTCPConnect) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress, - OnTCPConnectWithNetAddress) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_SSLHandshake, - OnTCPSSLHandshake) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Read, OnTCPRead) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Write, OnTCPWrite) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Disconnect, OnTCPDisconnect) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_SetOption, OnTCPSetOption) - - // NetworkMonitor messages. - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBNetworkMonitor_Start, - OnNetworkMonitorStart) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBNetworkMonitor_Stop, - OnNetworkMonitorStop) - // X509 certificate messages. IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBX509Certificate_ParseDER, OnX509CertificateParseDER); @@ -125,367 +27,14 @@ bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg, return handled; } -void PepperMessageFilter::OnIPAddressChanged() { - GetAndSendNetworkList(); -} - -net::HostResolver* PepperMessageFilter::GetHostResolver() { - return resource_context_ ? - resource_context_->GetHostResolver() : host_resolver_; -} - -net::CertVerifier* PepperMessageFilter::GetCertVerifier() { - if (!cert_verifier_) - cert_verifier_.reset(net::CertVerifier::CreateDefault()); - - return cert_verifier_.get(); -} - -net::TransportSecurityState* PepperMessageFilter::GetTransportSecurityState() { - if (!transport_security_state_) - transport_security_state_.reset(new net::TransportSecurityState); - - return transport_security_state_.get(); -} - -uint32 PepperMessageFilter::AddAcceptedTCPSocket( - int32 routing_id, - uint32 plugin_dispatcher_id, - net::StreamSocket* socket) { - scoped_ptr<net::StreamSocket> s(socket); - - uint32 tcp_socket_id = GenerateSocketID(); - if (tcp_socket_id != kInvalidSocketID) { - // Currently all TCP sockets created this way correspond to - // PPB_TCPSocket_Private. - tcp_sockets_[tcp_socket_id] = linked_ptr<PepperTCPSocket>( - new PepperTCPSocket(this, - routing_id, - plugin_dispatcher_id, - tcp_socket_id, - s.release(), - true /* private_api */)); - } - return tcp_socket_id; -} - -PepperMessageFilter::~PepperMessageFilter() { - if (!network_monitor_ids_.empty()) - net::NetworkChangeNotifier::RemoveIPAddressObserver(this); -} - -void PepperMessageFilter::OnTCPCreate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id) { - CreateTCPSocket(routing_id, plugin_dispatcher_id, false, socket_id); -} - -void PepperMessageFilter::OnTCPCreatePrivate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id) { - CreateTCPSocket(routing_id, plugin_dispatcher_id, true, socket_id); -} - -void PepperMessageFilter::OnTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - // This is only supported by PPB_TCPSocket_Private. - if (!iter->second->private_api()) { - NOTREACHED(); - return; - } - - content::SocketPermissionRequest params( - content::SocketPermissionRequest::TCP_CONNECT, host, port); - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::UI, FROM_HERE, - base::Bind(&PepperMessageFilter::CanUseSocketAPIs, this, - routing_id, params, true /* private_api */), - base::Bind(&PepperMessageFilter::DoTCPConnect, this, - routing_id, socket_id, host, port)); -} - -void PepperMessageFilter::DoTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port, - bool allowed) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - // Due to current permission check process (IO -> UI -> IO) some - // calls to the TCP socket interface can be intermixed (like - // Connect and Close). So, NOTREACHED() is not appropriate here. - return; - } - - if (routing_id == iter->second->routing_id() && allowed) - iter->second->Connect(host, port); - else - iter->second->SendConnectACKError(PP_ERROR_NOACCESS); -} - -void PepperMessageFilter::OnTCPConnectWithNetAddress( - int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - content::SocketPermissionRequest params = - pepper_socket_utils::CreateSocketPermissionRequest( - content::SocketPermissionRequest::TCP_CONNECT, net_addr); - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::UI, FROM_HERE, - base::Bind(&PepperMessageFilter::CanUseSocketAPIs, this, - routing_id, params, iter->second->private_api()), - base::Bind(&PepperMessageFilter::DoTCPConnectWithNetAddress, this, - routing_id, socket_id, net_addr)); -} - -void PepperMessageFilter::DoTCPConnectWithNetAddress( - int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr, - bool allowed) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - // Due to current permission check process (IO -> UI -> IO) some - // calls to the TCP socket interface can be intermixed (like - // ConnectWithNetAddress and Close). So, NOTREACHED() is not - // appropriate here. - return; - } - - if (routing_id == iter->second->routing_id() && allowed) - iter->second->ConnectWithNetAddress(net_addr); - else - iter->second->SendConnectACKError(PP_ERROR_NOACCESS); -} - -void PepperMessageFilter::OnTCPSSLHandshake( - uint32 socket_id, - const std::string& server_name, - uint16_t server_port, - const std::vector<std::vector<char> >& trusted_certs, - const std::vector<std::vector<char> >& untrusted_certs) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - // This is only supported by PPB_TCPSocket_Private. - if (!iter->second->private_api()) { - NOTREACHED(); - return; - } - - iter->second->SSLHandshake(server_name, server_port, trusted_certs, - untrusted_certs); -} - -void PepperMessageFilter::OnTCPRead(uint32 socket_id, int32_t bytes_to_read) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - iter->second->Read(bytes_to_read); -} - -void PepperMessageFilter::OnTCPWrite(uint32 socket_id, - const std::string& data) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - iter->second->Write(data); -} - -void PepperMessageFilter::OnTCPDisconnect(uint32 socket_id) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - // Destroying the TCPSocket instance will cancel any pending completion - // callback. From this point on, there won't be any messages associated with - // this socket sent to the plugin side. - tcp_sockets_.erase(iter); -} - -void PepperMessageFilter::OnTCPSetOption(uint32 socket_id, - PP_TCPSocket_Option name, - const ppapi::SocketOptionData& value) { - TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id); - if (iter == tcp_sockets_.end()) { - NOTREACHED(); - return; - } - - iter->second->SetOption(name, value); -} - -void PepperMessageFilter::OnNetworkMonitorStart(uint32 plugin_dispatcher_id) { - // Support all in-process plugins, and ones with "private" permissions. - if (plugin_type_ != PLUGIN_TYPE_IN_PROCESS && - !permissions_.HasPermission(ppapi::PERMISSION_PRIVATE)) { - return; - } - - if (network_monitor_ids_.empty()) - net::NetworkChangeNotifier::AddIPAddressObserver(this); - - network_monitor_ids_.insert(plugin_dispatcher_id); - GetAndSendNetworkList(); -} - -void PepperMessageFilter::OnNetworkMonitorStop(uint32 plugin_dispatcher_id) { - // Support all in-process plugins, and ones with "private" permissions. - if (plugin_type_ != PLUGIN_TYPE_IN_PROCESS && - !permissions_.HasPermission(ppapi::PERMISSION_PRIVATE)) { - return; - } - - network_monitor_ids_.erase(plugin_dispatcher_id); - if (network_monitor_ids_.empty()) - net::NetworkChangeNotifier::RemoveIPAddressObserver(this); -} - void PepperMessageFilter::OnX509CertificateParseDER( const std::vector<char>& der, bool* succeeded, ppapi::PPB_X509Certificate_Fields* result) { if (der.size() == 0) *succeeded = false; - *succeeded = PepperTCPSocket::GetCertificateFields(&der[0], der.size(), - result); -} - -uint32 PepperMessageFilter::GenerateSocketID() { - // TODO(yzshen): Change to use Pepper resource ID as socket ID. - // - // Generate a socket ID. For each process which sends us socket requests, IDs - // of living sockets must be unique, to each socket type. - // - // However, it is safe to generate IDs based on the internal state of a single - // PepperSocketMessageHandler object, because for each plugin or renderer - // process, there is at most one PepperMessageFilter (in other words, at most - // one PepperSocketMessageHandler) talking to it. - if (tcp_sockets_.size() >= kMaxSocketsAllowed) - return kInvalidSocketID; - - uint32 socket_id = kInvalidSocketID; - do { - // Although it is unlikely, make sure that we won't cause any trouble when - // the counter overflows. - socket_id = next_socket_id_++; - } while (socket_id == kInvalidSocketID || - tcp_sockets_.find(socket_id) != tcp_sockets_.end()); - - return socket_id; -} - -bool PepperMessageFilter::CanUseSocketAPIs( - int32 render_id, - const content::SocketPermissionRequest& params, - bool private_api) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // External plugins always get their own PepperMessageFilter, initialized with - // a render view id. Use this instead of the one that came with the message, - // which is actually an API ID. - bool external_plugin = false; - if (plugin_type_ == PLUGIN_TYPE_EXTERNAL_PLUGIN) { - external_plugin = true; - render_id = external_plugin_render_view_id_; - } - - RenderViewHostImpl* render_view_host = - RenderViewHostImpl::FromID(process_id_, render_id); - - return pepper_socket_utils::CanUseSocketAPIs(external_plugin, - private_api, - params, - render_view_host); -} - -void PepperMessageFilter::GetAndSendNetworkList() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - BrowserThread::PostBlockingPoolTask( - FROM_HERE, base::Bind(&PepperMessageFilter::DoGetNetworkList, this)); -} - -void PepperMessageFilter::DoGetNetworkList() { - scoped_ptr<net::NetworkInterfaceList> list(new net::NetworkInterfaceList()); - net::GetNetworkList(list.get()); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&PepperMessageFilter::SendNetworkList, - this, base::Passed(&list))); -} - -void PepperMessageFilter::SendNetworkList( - scoped_ptr<net::NetworkInterfaceList> list) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - scoped_ptr< ::ppapi::NetworkList> list_copy( - new ::ppapi::NetworkList(list->size())); - for (size_t i = 0; i < list->size(); ++i) { - const net::NetworkInterface& network = list->at(i); - ::ppapi::NetworkInfo& network_copy = list_copy->at(i); - network_copy.name = network.name; - - network_copy.addresses.resize(1, NetAddressPrivateImpl::kInvalidNetAddress); - bool result = NetAddressPrivateImpl::IPEndPointToNetAddress( - network.address, 0, &(network_copy.addresses[0])); - DCHECK(result); - - // TODO(sergeyu): Currently net::NetworkInterfaceList provides - // only name and one IP address. Add all other fields and copy - // them here. - network_copy.type = PP_NETWORKLIST_UNKNOWN; - network_copy.state = PP_NETWORKLIST_UP; - network_copy.display_name = network.name; - network_copy.mtu = 0; - } - for (NetworkMonitorIdSet::iterator it = network_monitor_ids_.begin(); - it != network_monitor_ids_.end(); ++it) { - Send(new PpapiMsg_PPBNetworkMonitor_NetworkList( - ppapi::API_ID_PPB_NETWORKMANAGER_PRIVATE, *it, *list_copy)); - } -} - -void PepperMessageFilter::CreateTCPSocket(int32 routing_id, - uint32 plugin_dispatcher_id, - bool private_api, - uint32* socket_id) { - *socket_id = GenerateSocketID(); - if (*socket_id == kInvalidSocketID) - return; - - tcp_sockets_[*socket_id] = linked_ptr<PepperTCPSocket>( - new PepperTCPSocket(this, routing_id, plugin_dispatcher_id, *socket_id, - private_api)); + *succeeded = + pepper_socket_utils::GetCertificateFields(&der[0], der.size(), result); } } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h index 11c8fbd4902..3f5010ed40e 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_message_filter.h @@ -5,223 +5,35 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_MESSAGE_FILTER_H_ #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_MESSAGE_FILTER_H_ -#include <map> -#include <string> #include <vector> #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" -#include "base/process/process.h" #include "content/public/browser/browser_message_filter.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/common/process_type.h" -#include "net/base/net_util.h" -#include "net/base/network_change_notifier.h" -#include "net/http/transport_security_state.h" -#include "net/socket/stream_socket.h" -#include "net/ssl/ssl_config_service.h" -#include "ppapi/c/pp_resource.h" -#include "ppapi/c/pp_stdint.h" -#include "ppapi/c/ppb_tcp_socket.h" -#include "ppapi/c/private/ppb_flash.h" -#include "ppapi/host/ppapi_host.h" -#include "ppapi/shared_impl/ppapi_permissions.h" - -struct PP_NetAddress_Private; - -namespace base { -class ListValue; -} - -namespace net { -class CertVerifier; -class HostResolver; -} namespace ppapi { class PPB_X509Certificate_Fields; -class SocketOptionData; } namespace content { -class BrowserContext; -class PepperTCPSocket; -class ResourceContext; -// This class is used in two contexts, both supporting PPAPI plugins. The first -// is on the renderer->browser channel, to handle requests from in-process -// PPAPI plugins and any requests that the PPAPI implementation code in the -// renderer needs to make. The second is on the plugin->browser channel to -// handle requests that out-of-process plugins send directly to the browser. -class PepperMessageFilter - : public BrowserMessageFilter, - public net::NetworkChangeNotifier::IPAddressObserver { +// Message filter that handles IPC for PPB_X509Certificate_Private. +class PepperMessageFilter : public BrowserMessageFilter { public: - // Constructor when used in the context of a render process. - PepperMessageFilter(int process_id, - BrowserContext* browser_context); - - // Constructor when used in the context of a PPAPI process.. - PepperMessageFilter(const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver); - - // Constructor when used in the context of an external plugin, i.e. created by - // the embedder using BrowserPpapiHost::CreateExternalPluginProcess. - PepperMessageFilter(const ppapi::PpapiPermissions& permissions, - net::HostResolver* host_resolver, - int process_id, - int render_view_id); + PepperMessageFilter(); // BrowserMessageFilter methods. virtual bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok) OVERRIDE; - // net::NetworkChangeNotifier::IPAddressObserver interface. - virtual void OnIPAddressChanged() OVERRIDE; - - // Returns the host resolver (it may come from the resource context or the - // host_resolver_ member). - net::HostResolver* GetHostResolver(); - - net::CertVerifier* GetCertVerifier(); - net::TransportSecurityState* GetTransportSecurityState(); - - // Adds already accepted socket to the internal TCP sockets table. Takes - // ownership over |socket|. In the case of failure (full socket table) - // returns 0 and deletes |socket|. Otherwise, returns generated ID for - // |socket|. - uint32 AddAcceptedTCPSocket(int32 routing_id, - uint32 plugin_dispatcher_id, - net::StreamSocket* socket); - - const net::SSLConfig& ssl_config() { return ssl_config_; } - protected: virtual ~PepperMessageFilter(); private: - struct OnConnectTcpBoundInfo { - int routing_id; - int request_id; - }; - - // Containers for sockets keyed by socked_id. - typedef std::map<uint32, linked_ptr<PepperTCPSocket> > TCPSocketMap; - - // Set of disptachers ID's that have subscribed for NetworkMonitor - // notifications. - typedef std::set<uint32> NetworkMonitorIdSet; - - void OnGetLocalTimeZoneOffset(base::Time t, double* result); - - void OnTCPCreate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id); - void OnTCPCreatePrivate(int32 routing_id, - uint32 plugin_dispatcher_id, - uint32* socket_id); - void OnTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port); - void OnTCPConnectWithNetAddress(int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr); - void OnTCPSSLHandshake( - uint32 socket_id, - const std::string& server_name, - uint16_t server_port, - const std::vector<std::vector<char> >& trusted_certs, - const std::vector<std::vector<char> >& untrusted_certs); - void OnTCPRead(uint32 socket_id, int32_t bytes_to_read); - void OnTCPWrite(uint32 socket_id, const std::string& data); - void OnTCPDisconnect(uint32 socket_id); - void OnTCPSetOption(uint32 socket_id, - PP_TCPSocket_Option name, - const ppapi::SocketOptionData& value); - - void OnNetworkMonitorStart(uint32 plugin_dispatcher_id); - void OnNetworkMonitorStop(uint32 plugin_dispatcher_id); - - void DoTCPConnect(int32 routing_id, - uint32 socket_id, - const std::string& host, - uint16_t port, - bool allowed); - void DoTCPConnectWithNetAddress(int32 routing_id, - uint32 socket_id, - const PP_NetAddress_Private& net_addr, - bool allowed); void OnX509CertificateParseDER(const std::vector<char>& der, bool* succeeded, ppapi::PPB_X509Certificate_Fields* result); - void OnUpdateActivity(); - - uint32 GenerateSocketID(); - - // Return true if render with given ID can use socket APIs. - bool CanUseSocketAPIs(int32 render_id, - const content::SocketPermissionRequest& params, - bool private_api); - - void GetAndSendNetworkList(); - void DoGetNetworkList(); - void SendNetworkList(scoped_ptr<net::NetworkInterfaceList> list); - void CreateTCPSocket(int32 routing_id, - uint32 plugin_dispatcher_id, - bool private_api, - uint32* socket_id); - enum PluginType { - PLUGIN_TYPE_IN_PROCESS, - PLUGIN_TYPE_OUT_OF_PROCESS, - // External plugin means it was created through - // BrowserPpapiHost::CreateExternalPluginProcess. - PLUGIN_TYPE_EXTERNAL_PLUGIN, - }; - - PluginType plugin_type_; - - // When attached to an out-of-process plugin (be it native or NaCl) this - // will have the Pepper permissions for the plugin. When attached to the - // renderer channel, this will have no permissions listed (since there may - // be many plugins sharing this channel). - ppapi::PpapiPermissions permissions_; - - // Render process ID. - int process_id_; - - // External plugin RenderView id to determine private API access. Normally, we - // handle messages coming from multiple RenderViews, but external plugins - // always creates a new PepperMessageFilter for each RenderView. - int external_plugin_render_view_id_; - - // When non-NULL, this should be used instead of the host_resolver_. - ResourceContext* const resource_context_; - - // When non-NULL, this should be used instead of the resource_context_. Use - // GetHostResolver instead of accessing directly. - net::HostResolver* host_resolver_; - - // The default SSL configuration settings are used, as opposed to Chrome's SSL - // settings. - net::SSLConfig ssl_config_; - // This is lazily created. Users should use GetCertVerifier to retrieve it. - scoped_ptr<net::CertVerifier> cert_verifier_; - // This is lazily created. Users should use GetTransportSecurityState to - // retrieve it. - scoped_ptr<net::TransportSecurityState> transport_security_state_; - - uint32 next_socket_id_; - - TCPSocketMap tcp_sockets_; - - NetworkMonitorIdSet network_monitor_ids_; - - base::FilePath browser_path_; - bool incognito_; DISALLOW_COPY_AND_ASSIGN(PepperMessageFilter); }; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc new file mode 100644 index 00000000000..b53a60b779b --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.cc @@ -0,0 +1,121 @@ +// Copyright 2013 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/browser/renderer_host/pepper/pepper_network_monitor_host.h" + +#include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" +#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/pepper_socket_utils.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/socket_permission_request.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/private/net_address_private_impl.h" + + +namespace content { + +namespace { + +bool CanUseNetworkMonitor(bool external_plugin, + int render_process_id, + int render_view_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + SocketPermissionRequest request = SocketPermissionRequest( + SocketPermissionRequest::NETWORK_STATE, std::string(), 0); + return pepper_socket_utils::CanUseSocketAPIs( + external_plugin, false /* private_api */, &request, render_process_id, + render_view_id); +} + +scoped_ptr<net::NetworkInterfaceList> GetNetworkList() { + scoped_ptr<net::NetworkInterfaceList> list(new net::NetworkInterfaceList()); + net::GetNetworkList(list.get()); + return list.Pass(); +} + +} // namespace + +PepperNetworkMonitorHost::PepperNetworkMonitorHost( + BrowserPpapiHostImpl* host, + PP_Instance instance, + PP_Resource resource) + : ResourceHost(host->GetPpapiHost(), instance, resource), + weak_factory_(this) { + int render_process_id; + int render_view_id; + host->GetRenderViewIDsForInstance(instance, + &render_process_id, + &render_view_id); + + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::UI, FROM_HERE, + base::Bind(&CanUseNetworkMonitor, host->external_plugin(), + render_process_id, render_view_id), + base::Bind(&PepperNetworkMonitorHost::OnPermissionCheckResult, + weak_factory_.GetWeakPtr())); +} + +PepperNetworkMonitorHost::~PepperNetworkMonitorHost() { + net::NetworkChangeNotifier::RemoveIPAddressObserver(this); +} + +void PepperNetworkMonitorHost::OnIPAddressChanged() { + GetAndSendNetworkList(); +} + +void PepperNetworkMonitorHost::OnPermissionCheckResult( + bool can_use_network_monitor) { + if (!can_use_network_monitor) { + host()->SendUnsolicitedReply(pp_resource(), + PpapiPluginMsg_NetworkMonitor_Forbidden()); + return; + } + + net::NetworkChangeNotifier::AddIPAddressObserver(this); + GetAndSendNetworkList(); +} + +void PepperNetworkMonitorHost::GetAndSendNetworkList() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Call GetNetworkList() on a thread that allows blocking IO. + base::PostTaskAndReplyWithResult( + BrowserThread::GetBlockingPool(), FROM_HERE, + base::Bind(&GetNetworkList), + base::Bind(&PepperNetworkMonitorHost::SendNetworkList, + weak_factory_.GetWeakPtr())); +} + +void PepperNetworkMonitorHost::SendNetworkList( + scoped_ptr<net::NetworkInterfaceList> list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + scoped_ptr<ppapi::proxy::SerializedNetworkList> list_copy( + new ppapi::proxy::SerializedNetworkList(list->size())); + for (size_t i = 0; i < list->size(); ++i) { + const net::NetworkInterface& network = list->at(i); + ppapi::proxy::SerializedNetworkInfo& network_copy = list_copy->at(i); + network_copy.name = network.name; + + network_copy.addresses.resize( + 1, ppapi::NetAddressPrivateImpl::kInvalidNetAddress); + bool result = ppapi::NetAddressPrivateImpl::IPEndPointToNetAddress( + network.address, 0, &(network_copy.addresses[0])); + DCHECK(result); + + // TODO(sergeyu): Currently net::NetworkInterfaceList provides + // only name and one IP address. Add all other fields and copy + // them here. + network_copy.type = PP_NETWORKLIST_TYPE_UNKNOWN; + network_copy.state = PP_NETWORKLIST_STATE_UP; + network_copy.display_name = network.name; + network_copy.mtu = 0; + } + host()->SendUnsolicitedReply( + pp_resource(), PpapiPluginMsg_NetworkMonitor_NetworkList(*list_copy)); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h new file mode 100644 index 00000000000..e27f8a2ac21 --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_network_monitor_host.h @@ -0,0 +1,50 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_PEPPER_PEPPER_NETWORK_MONITOR_HOST_H_ +#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_NETWORK_MONITOR_HOST_H_ + +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "content/common/content_export.h" +#include "net/base/net_util.h" +#include "net/base/network_change_notifier.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/host/resource_host.h" + +namespace content { + +class BrowserPpapiHostImpl; + +// The host for PPB_NetworkMonitor. This class lives on the IO thread. +class CONTENT_EXPORT PepperNetworkMonitorHost + : public ppapi::host::ResourceHost, + public net::NetworkChangeNotifier::IPAddressObserver { + public: + PepperNetworkMonitorHost( + BrowserPpapiHostImpl* host, + PP_Instance instance, + PP_Resource resource); + + virtual ~PepperNetworkMonitorHost(); + + // net::NetworkChangeNotifier::IPAddressObserver interface. + virtual void OnIPAddressChanged() OVERRIDE; + + private: + void OnPermissionCheckResult(bool can_use_network_monitor); + + void GetAndSendNetworkList(); + void SendNetworkList(scoped_ptr<net::NetworkInterfaceList> list); + + ppapi::host::ReplyMessageContext reply_context_; + + base::WeakPtrFactory<PepperNetworkMonitorHost> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperNetworkMonitorHost); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_NETWORK_MONITOR_HOST_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc index 6042badfc92..13f0fc3733f 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc @@ -84,7 +84,7 @@ PepperNetworkProxyHost::GetUIThreadDataOnUIThread(int render_process_id, result.is_allowed = pepper_socket_utils::CanUseSocketAPIs( is_external_plugin, false /* is_private_api */, - request, + &request, render_view_host); } return result; diff --git a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc index a5b95ceb223..e4c8aa71dc1 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.cc @@ -16,6 +16,7 @@ #include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_message_utils.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/ppapi_message_utils.h" #include "ppapi/proxy/resource_message_params.h" namespace content { @@ -28,8 +29,7 @@ PepperRendererConnection::PepperRendererConnection(int render_process_id) "", base::FilePath(), base::FilePath(), - false, - NULL)); + false)); } PepperRendererConnection::~PepperRendererConnection() { @@ -72,10 +72,8 @@ bool PepperRendererConnection::OnMessageReceived(const IPC::Message& msg, bool handled = true; IPC_BEGIN_MESSAGE_MAP_EX(PepperRendererConnection, msg, *message_was_ok) - IPC_MESSAGE_HANDLER(PpapiHostMsg_CreateResourceHostFromHost, - OnMsgCreateResourceHostFromHost) - IPC_MESSAGE_HANDLER(PpapiHostMsg_FileRef_GetInfoForRenderer, - OnMsgFileRefGetInfoForRenderer) + IPC_MESSAGE_HANDLER(PpapiHostMsg_CreateResourceHostsFromHost, + OnMsgCreateResourceHostsFromHost) IPC_MESSAGE_HANDLER(ViewHostMsg_DidCreateInProcessInstance, OnMsgDidCreateInProcessInstance) IPC_MESSAGE_HANDLER(ViewHostMsg_DidDeleteInProcessInstance, @@ -86,79 +84,48 @@ bool PepperRendererConnection::OnMessageReceived(const IPC::Message& msg, return handled; } -void PepperRendererConnection::OnMsgCreateResourceHostFromHost( +void PepperRendererConnection::OnMsgCreateResourceHostsFromHost( int routing_id, int child_process_id, const ppapi::proxy::ResourceMessageCallParams& params, PP_Instance instance, - const IPC::Message& nested_msg) { + const std::vector<IPC::Message>& nested_msgs) { BrowserPpapiHostImpl* host = GetHostForChildProcess(child_process_id); - int pending_resource_host_id; + std::vector<int> pending_resource_host_ids(nested_msgs.size(), 0); if (!host) { DLOG(ERROR) << "Invalid plugin process ID."; - pending_resource_host_id = 0; } else { - // FileRef_CreateExternal is only permitted from the renderer. Because of - // this, we handle this message here and not in - // content_browser_pepper_host_factory.cc. - scoped_ptr<ppapi::host::ResourceHost> resource_host; - if (host->IsValidInstance(instance)) { - if (nested_msg.type() == PpapiHostMsg_FileRef_CreateExternal::ID) { - base::FilePath external_path; - if (ppapi::UnpackMessage<PpapiHostMsg_FileRef_CreateExternal>( - nested_msg, &external_path)) { - resource_host.reset(new PepperFileRefHost( - host, instance, params.pp_resource(), external_path)); + for (size_t i = 0; i < nested_msgs.size(); ++i) { + // FileRef_CreateExternal is only permitted from the renderer. Because of + // this, we handle this message here and not in + // content_browser_pepper_host_factory.cc. + scoped_ptr<ppapi::host::ResourceHost> resource_host; + if (host->IsValidInstance(instance)) { + if (nested_msgs[i].type() == PpapiHostMsg_FileRef_CreateExternal::ID) { + base::FilePath external_path; + if (ppapi::UnpackMessage<PpapiHostMsg_FileRef_CreateExternal>( + nested_msgs[i], &external_path)) { + resource_host.reset(new PepperFileRefHost( + host, instance, params.pp_resource(), external_path)); + } } } - } - - if (!resource_host.get()) { - resource_host = host->GetPpapiHost()->CreateResourceHost(params, - instance, - nested_msg); - } - pending_resource_host_id = - host->GetPpapiHost()->AddPendingResourceHost(resource_host.Pass()); - } - - Send(new PpapiHostMsg_CreateResourceHostFromHostReply( - routing_id, params.sequence(), pending_resource_host_id)); -} -void PepperRendererConnection::OnMsgFileRefGetInfoForRenderer( - int routing_id, - int child_process_id, - int32_t sequence, - const std::vector<PP_Resource>& resources) { - std::vector<PP_Resource> out_resources; - std::vector<PP_FileSystemType> fs_types; - std::vector<std::string> file_system_url_specs; - std::vector<base::FilePath> external_paths; + if (!resource_host.get()) { + resource_host = host->GetPpapiHost()->CreateResourceHost( + params, instance, nested_msgs[i]); + } - BrowserPpapiHostImpl* host = GetHostForChildProcess(child_process_id); - if (host) { - for (size_t i = 0; i < resources.size(); ++i) { - ppapi::host::ResourceHost* resource_host = - host->GetPpapiHost()->GetResourceHost(resources[i]); - if (resource_host && resource_host->IsFileRefHost()) { - PepperFileRefHost* file_ref_host = - static_cast<PepperFileRefHost*>(resource_host); - out_resources.push_back(resources[i]); - fs_types.push_back(file_ref_host->GetFileSystemType()); - file_system_url_specs.push_back(file_ref_host->GetFileSystemURLSpec()); - external_paths.push_back(file_ref_host->GetExternalPath()); + if (resource_host.get()) { + pending_resource_host_ids[i] = + host->GetPpapiHost()->AddPendingResourceHost(resource_host.Pass()); } } } - Send(new PpapiHostMsg_FileRef_GetInfoForRendererReply( - routing_id, - sequence, - out_resources, - fs_types, - file_system_url_specs, - external_paths)); + + Send(new PpapiHostMsg_CreateResourceHostsFromHostReply( + routing_id, params.sequence(), pending_resource_host_ids)); } void PepperRendererConnection::OnMsgDidCreateInProcessInstance( diff --git a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h index 43923465d0b..157827a9cfd 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_renderer_connection.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_RENDERER_CONNECTION_H_ #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_RENDERER_CONNECTION_H_ +#include <vector> + #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" @@ -45,18 +47,12 @@ class PepperRendererConnection : public BrowserMessageFilter { // PepperRendererConnection, which serves as the host for in-process plugins. BrowserPpapiHostImpl* GetHostForChildProcess(int child_process_id) const; - void OnMsgCreateResourceHostFromHost( + void OnMsgCreateResourceHostsFromHost( int routing_id, int child_process_id, const ppapi::proxy::ResourceMessageCallParams& params, PP_Instance instance, - const IPC::Message& nested_msg); - - void OnMsgFileRefGetInfoForRenderer( - int routing_id, - int child_process_id, - int32_t sequence_num, - const std::vector<PP_Resource>& resources); + const std::vector<IPC::Message>& nested_msgs); void OnMsgDidCreateInProcessInstance( PP_Instance instance, diff --git a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc index 5402823f01e..5816ce4831f 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.cc @@ -10,8 +10,18 @@ namespace content { -bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, - const base::FilePath& file) { +namespace { + +template <typename CanRead, typename CanWrite, + typename CanCreate, typename CanCreateWrite, + typename FileID> +bool CanOpenFileWithPepperFlags(CanRead can_read, + CanWrite can_write, + CanCreate can_create, + CanCreateWrite can_create_write, + int pp_open_flags, + int child_id, + const FileID& file) { ChildProcessSecurityPolicyImpl* policy = ChildProcessSecurityPolicyImpl::GetInstance(); @@ -22,33 +32,53 @@ bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, bool pp_exclusive = !!(pp_open_flags & PP_FILEOPENFLAG_EXCLUSIVE); bool pp_append = !!(pp_open_flags & PP_FILEOPENFLAG_APPEND); - if (pp_read && !policy->CanReadFile(child_id, file)) + if (pp_read && !(policy->*can_read)(child_id, file)) return false; - if (pp_write && !policy->CanWriteFile(child_id, file)) + if (pp_write && !(policy->*can_write)(child_id, file)) return false; - if (pp_append) { - // Given ChildSecurityPolicyImpl's current definition of permissions, - // APPEND is never supported. + // TODO(tommycli): Maybe tighten up required permission. crbug.com/284792 + if (pp_append && !(policy->*can_create_write)(child_id, file)) return false; - } if (pp_truncate && !pp_write) return false; if (pp_create) { if (pp_exclusive) { - return policy->CanCreateFile(child_id, file); + return (policy->*can_create)(child_id, file); } else { // Asks for too much, but this is the only grant that allows overwrite. - return policy->CanCreateWriteFile(child_id, file); + return (policy->*can_create_write)(child_id, file); } } else if (pp_truncate) { - return policy->CanCreateWriteFile(child_id, file); + return (policy->*can_create_write)(child_id, file); } return true; } +} + +bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, + const base::FilePath& file) { + return CanOpenFileWithPepperFlags( + &ChildProcessSecurityPolicyImpl::CanReadFile, + &ChildProcessSecurityPolicyImpl::CanWriteFile, + &ChildProcessSecurityPolicyImpl::CanCreateFile, + &ChildProcessSecurityPolicyImpl::CanCreateWriteFile, + pp_open_flags, child_id, file); +} + +bool CanOpenFileSystemURLWithPepperFlags(int pp_open_flags, int child_id, + const fileapi::FileSystemURL& url) { + return CanOpenFileWithPepperFlags( + &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, + &ChildProcessSecurityPolicyImpl::CanWriteFileSystemFile, + &ChildProcessSecurityPolicyImpl::CanCreateFileSystemFile, + &ChildProcessSecurityPolicyImpl::CanCreateWriteFileSystemFile, + pp_open_flags, child_id, url); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h index 4a3cea5874a..d0a39831324 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_security_helper.h @@ -7,15 +7,23 @@ #include "base/files/file_path.h" #include "content/common/content_export.h" +#include "webkit/browser/fileapi/file_system_url.h" namespace content { -// Helper method that returns whether or not the child process is allowed to +// Helper function that returns whether or not the child process is allowed to // open the specified |file| with the specified |pp_open_flags|. CONTENT_EXPORT bool CanOpenWithPepperFlags(int pp_open_flags, int child_id, const base::FilePath& file); +// Helper function that returns whether or not the child process is allowed to +// open the specified file system |url| with the specified |pp_open_flags|. +CONTENT_EXPORT bool CanOpenFileSystemURLWithPepperFlags( + int pp_open_flags, + int child_id, + const fileapi::FileSystemURL& url); + } // namespace content #endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_SECURITY_HELPER_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc index 9dc585e3315..2f109cddb71 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.cc @@ -8,13 +8,16 @@ #include <vector> #include "base/logging.h" +#include "base/memory/ref_counted.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/site_instance.h" #include "content/public/common/content_client.h" +#include "net/cert/x509_certificate.h" #include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/shared_impl/private/net_address_private_impl.h" +#include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h" namespace content { namespace pepper_socket_utils { @@ -34,7 +37,7 @@ SocketPermissionRequest CreateSocketPermissionRequest( bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, int render_process_id, int render_view_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -48,7 +51,7 @@ bool CanUseSocketAPIs(bool external_plugin, bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, RenderViewHost* render_view_host) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -77,5 +80,60 @@ bool CanUseSocketAPIs(bool external_plugin, return true; } +bool GetCertificateFields(const net::X509Certificate& cert, + ppapi::PPB_X509Certificate_Fields* fields) { + const net::CertPrincipal& issuer = cert.issuer(); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COMMON_NAME, + new base::StringValue(issuer.common_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_LOCALITY_NAME, + new base::StringValue(issuer.locality_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_STATE_OR_PROVINCE_NAME, + new base::StringValue(issuer.state_or_province_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COUNTRY_NAME, + new base::StringValue(issuer.country_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_NAME, + new base::StringValue(JoinString(issuer.organization_names, '\n'))); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_UNIT_NAME, + new base::StringValue(JoinString(issuer.organization_unit_names, '\n'))); + + const net::CertPrincipal& subject = cert.subject(); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COMMON_NAME, + new base::StringValue(subject.common_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_LOCALITY_NAME, + new base::StringValue(subject.locality_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_STATE_OR_PROVINCE_NAME, + new base::StringValue(subject.state_or_province_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COUNTRY_NAME, + new base::StringValue(subject.country_name)); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_NAME, + new base::StringValue(JoinString(subject.organization_names, '\n'))); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_UNIT_NAME, + new base::StringValue(JoinString(subject.organization_unit_names, '\n'))); + + const std::string& serial_number = cert.serial_number(); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_SERIAL_NUMBER, + base::BinaryValue::CreateWithCopiedBuffer(serial_number.data(), + serial_number.length())); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_BEFORE, + new base::FundamentalValue(cert.valid_start().ToDoubleT())); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_AFTER, + new base::FundamentalValue(cert.valid_expiry().ToDoubleT())); + std::string der; + net::X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der); + fields->SetField(PP_X509CERTIFICATE_PRIVATE_RAW, + base::BinaryValue::CreateWithCopiedBuffer(der.data(), der.length())); + return true; +} + +bool GetCertificateFields(const char* der, + uint32_t length, + ppapi::PPB_X509Certificate_Fields* fields) { + scoped_refptr<net::X509Certificate> cert = + net::X509Certificate::CreateFromBytes(der, length); + if (!cert.get()) + return false; + return GetCertificateFields(*cert.get(), fields); +} + } // namespace pepper_socket_utils } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h index 7a0cef56a5c..1a1a26cac19 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_socket_utils.h @@ -6,9 +6,18 @@ #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_SOCKET_UTILS_H_ #include "content/public/common/socket_permission_request.h" +#include "ppapi/c/pp_stdint.h" struct PP_NetAddress_Private; +namespace net { +class X509Certificate; +} + +namespace ppapi { +class PPB_X509Certificate_Fields; +} + namespace content { class RenderViewHost; @@ -19,18 +28,32 @@ SocketPermissionRequest CreateSocketPermissionRequest( SocketPermissionRequest::OperationType type, const PP_NetAddress_Private& net_addr); +// Returns true if the socket operation specified by |params| is allowed. +// If |params| is NULL, this method checks the basic "socket" permission, which +// is for those operations that don't require a specific socket permission rule. bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, int render_process_id, int render_view_id); // TODO (ygorshenin@): remove this method. bool CanUseSocketAPIs(bool external_plugin, bool private_api, - const SocketPermissionRequest& params, + const SocketPermissionRequest* params, RenderViewHost* render_view_host); +// Extracts the certificate field data from a net::X509Certificate into +// PPB_X509Certificate_Fields. +bool GetCertificateFields(const net::X509Certificate& cert, + ppapi::PPB_X509Certificate_Fields* fields); + +// Extracts the certificate field data from the DER representation of a +// certificate into PPB_X509Certificate_Fields. +bool GetCertificateFields(const char* der, + uint32_t length, + ppapi::PPB_X509Certificate_Fields* fields); + } // namespace pepper_socket_utils } // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc index 48c8291cab9..063c11ed784 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc @@ -8,20 +8,22 @@ #include "base/bind_helpers.h" #include "base/logging.h" #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" #include "content/browser/renderer_host/pepper/pepper_socket_utils.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/socket_permission_request.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" -#include "net/socket/tcp_client_socket.h" -#include "net/socket/tcp_server_socket.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/private/ppb_net_address_private.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/error_conversion.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/api_id.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" #include "ppapi/shared_impl/private/net_address_private_impl.h" using ppapi::NetAddressPrivateImpl; @@ -36,18 +38,21 @@ size_t g_num_instances = 0; namespace content { PepperTCPServerSocketMessageFilter::PepperTCPServerSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter) - : state_(STATE_BEFORE_LISTENING), - pepper_message_filter_(pepper_message_filter), + bool private_api) + : ppapi_host_(host->GetPpapiHost()), + factory_(factory), + instance_(instance), + state_(STATE_BEFORE_LISTENING), external_plugin_(host->external_plugin()), private_api_(private_api), render_process_id_(0), render_view_id_(0) { ++g_num_instances; - DCHECK(host); + DCHECK(factory_); + DCHECK(ppapi_host_); if (!host->GetRenderViewIDsForInstance(instance, &render_process_id_, &render_view_id_)) { @@ -83,7 +88,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnResourceMessageReceived( IPC_BEGIN_MESSAGE_MAP(PepperTCPServerSocketMessageFilter, msg) PPAPI_DISPATCH_HOST_RESOURCE_CALL( PpapiHostMsg_TCPServerSocket_Listen, OnMsgListen) - PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( PpapiHostMsg_TCPServerSocket_Accept, OnMsgAccept) PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( PpapiHostMsg_TCPServerSocket_StopListening, OnMsgStopListening) @@ -103,7 +108,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgListen( content::SocketPermissionRequest::TCP_LISTEN, addr); if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, + &request, render_process_id_, render_view_id_)) { return PP_ERROR_NOACCESS; @@ -117,8 +122,7 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgListen( } int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept( - const ppapi::host::HostMessageContext* context, - uint32 plugin_dispatcher_id) { + const ppapi::host::HostMessageContext* context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(context); @@ -129,12 +133,12 @@ int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept( ppapi::host::ReplyMessageContext reply_context( context->MakeReplyMessageContext()); int net_result = socket_->Accept( - &socket_buffer_, + &accepted_socket_, + &accepted_address_, base::Bind(&PepperTCPServerSocketMessageFilter::OnAcceptCompleted, - base::Unretained(this), - reply_context, plugin_dispatcher_id)); + base::Unretained(this), reply_context)); if (net_result != net::ERR_IO_PENDING) - OnAcceptCompleted(reply_context, plugin_dispatcher_id, net_result); + OnAcceptCompleted(reply_context, net_result); return PP_OK_COMPLETIONPENDING; } @@ -165,8 +169,22 @@ void PepperTCPServerSocketMessageFilter::DoListen( state_ = STATE_LISTEN_IN_PROGRESS; - socket_.reset(new net::TCPServerSocket(NULL, net::NetLog::Source())); - int net_result = socket_->Listen(net::IPEndPoint(address, port), backlog); + socket_.reset(new net::TCPSocket(NULL, net::NetLog::Source())); + int net_result = net::OK; + do { + net::IPEndPoint ip_end_point(address, port); + net_result = socket_->Open(ip_end_point.GetFamily()); + if (net_result != net::OK) + break; + net_result = socket_->SetDefaultOptionsForServer(); + if (net_result != net::OK) + break; + net_result = socket_->Bind(ip_end_point); + if (net_result != net::OK) + break; + net_result = socket_->Listen(backlog); + } while (false); + if (net_result != net::ERR_IO_PENDING) OnListenCompleted(context, net_result); } @@ -211,7 +229,6 @@ void PepperTCPServerSocketMessageFilter::OnListenCompleted( void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( const ppapi::host::ReplyMessageContext& context, - uint32 plugin_dispatcher_id, int net_result) { if (state_ != STATE_ACCEPT_IN_PROGRESS) { SendAcceptError(context, PP_ERROR_FAILED); @@ -226,16 +243,15 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( return; } - DCHECK(socket_buffer_.get()); + DCHECK(accepted_socket_.get()); - scoped_ptr<net::StreamSocket> socket(socket_buffer_.release()); net::IPEndPoint ip_end_point_local; - net::IPEndPoint ip_end_point_remote; PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress; PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress; int32_t pp_result = - NetErrorToPepperError(socket->GetLocalAddress(&ip_end_point_local)); + NetErrorToPepperError(accepted_socket_->GetLocalAddress( + &ip_end_point_local)); if (pp_result != PP_OK) { SendAcceptError(context, pp_result); return; @@ -243,33 +259,27 @@ void PepperTCPServerSocketMessageFilter::OnAcceptCompleted( if (!NetAddressPrivateImpl::IPEndPointToNetAddress( ip_end_point_local.address(), ip_end_point_local.port(), - &local_addr)) { - SendAcceptError(context, PP_ERROR_FAILED); - return; - } - pp_result = - NetErrorToPepperError(socket->GetPeerAddress(&ip_end_point_remote)); - if (pp_result != PP_OK) { - SendAcceptError(context, pp_result); - return; - } - if (!NetAddressPrivateImpl::IPEndPointToNetAddress( - ip_end_point_remote.address(), - ip_end_point_remote.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + accepted_address_.address(), + accepted_address_.port(), &remote_addr)) { SendAcceptError(context, PP_ERROR_FAILED); return; } - if (!pepper_message_filter_.get() || plugin_dispatcher_id == 0) { - SendAcceptError(context, PP_ERROR_FAILED); + + scoped_ptr<ppapi::host::ResourceHost> host = + factory_->CreateAcceptedTCPSocket( + instance_, ppapi::TCP_SOCKET_VERSION_PRIVATE, + accepted_socket_.Pass()); + if (!host) { + SendAcceptError(context, PP_ERROR_NOSPACE); return; } - uint32 accepted_socket_id = pepper_message_filter_->AddAcceptedTCPSocket( - ppapi::API_ID_PPB_TCPSOCKET_PRIVATE, - plugin_dispatcher_id, - socket.release()); - if (accepted_socket_id != 0) { - SendAcceptReply(context, PP_OK, accepted_socket_id, local_addr, + int pending_resource_id = ppapi_host_->AddPendingResourceHost(host.Pass()); + if (pending_resource_id) { + SendAcceptReply(context, PP_OK, pending_resource_id, + local_addr, remote_addr); } else { SendAcceptError(context, PP_ERROR_NOSPACE); @@ -296,13 +306,13 @@ void PepperTCPServerSocketMessageFilter::SendListenError( void PepperTCPServerSocketMessageFilter::SendAcceptReply( const ppapi::host::ReplyMessageContext& context, int32_t pp_result, - uint32 accepted_socket_id, + int pending_resource_id, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr) { ppapi::host::ReplyMessageContext reply_context(context); reply_context.params.set_result(pp_result); SendReply(reply_context, PpapiPluginMsg_TCPServerSocket_AcceptReply( - accepted_socket_id, local_addr, remote_addr)); + pending_resource_id, local_addr, remote_addr)); } void PepperTCPServerSocketMessageFilter::SendAcceptError( diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h index ad8cf19f287..5d17333c9e8 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h @@ -9,30 +9,35 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "content/browser/renderer_host/pepper/pepper_message_filter.h" #include "content/common/content_export.h" +#include "net/base/ip_endpoint.h" +#include "net/socket/tcp_socket.h" #include "ppapi/c/pp_instance.h" #include "ppapi/host/resource_message_filter.h" struct PP_NetAddress_Private; -namespace net { -class ServerSocket; -class StreamSocket; +namespace ppapi { +namespace host { +class PpapiHost; +} } namespace content { class BrowserPpapiHostImpl; +class ContentBrowserPepperHostFactory; +// TODO(yzshen): Remove this class entirely and let +// TCPServerSocketPrivateResource inherit TCPSocketResourceBase. class CONTENT_EXPORT PepperTCPServerSocketMessageFilter : public ppapi::host::ResourceMessageFilter { public: PepperTCPServerSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, BrowserPpapiHostImpl* host, PP_Instance instance, - bool private_api, - const scoped_refptr<PepperMessageFilter>& pepper_message_filter); + bool private_api); static size_t GetNumInstances(); @@ -58,8 +63,7 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter int32_t OnMsgListen(const ppapi::host::HostMessageContext* context, const PP_NetAddress_Private& addr, int32_t backlog); - int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context, - uint32 plugin_dispatcher_id); + int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context); int32_t OnMsgStopListening(const ppapi::host::HostMessageContext* context); void DoListen(const ppapi::host::ReplyMessageContext& context, @@ -69,7 +73,6 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter void OnListenCompleted(const ppapi::host::ReplyMessageContext& context, int net_result); void OnAcceptCompleted(const ppapi::host::ReplyMessageContext& context, - uint32 plugin_dispatcher_id, int net_result); void SendListenReply(const ppapi::host::ReplyMessageContext& context, @@ -79,17 +82,23 @@ class CONTENT_EXPORT PepperTCPServerSocketMessageFilter int32_t pp_result); void SendAcceptReply(const ppapi::host::ReplyMessageContext& context, int32_t pp_result, - uint32 accepted_socket_id, + int pending_resource_id, const PP_NetAddress_Private& local_addr, const PP_NetAddress_Private& remote_addr); void SendAcceptError(const ppapi::host::ReplyMessageContext& context, int32_t pp_result); // Following fields are initialized and used only on the IO thread. + // Non-owning ptr. + ppapi::host::PpapiHost* ppapi_host_; + // Non-owning ptr. + ContentBrowserPepperHostFactory* factory_; + PP_Instance instance_; + State state_; - scoped_ptr<net::ServerSocket> socket_; - scoped_ptr<net::StreamSocket> socket_buffer_; - scoped_refptr<PepperMessageFilter> pepper_message_filter_; + scoped_ptr<net::TCPSocket> socket_; + scoped_ptr<net::TCPSocket> accepted_socket_; + net::IPEndPoint accepted_address_; // Following fields are initialized on the IO thread but used only // on the UI thread. diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc index f05be8783ed..c5b752aa7f5 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.cc @@ -87,10 +87,12 @@ void PepperTCPSocket::Connect(const std::string& host, uint16_t port) { connection_state_ = CONNECT_IN_PROGRESS; net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port)); - resolver_.reset(new net::SingleRequestHostResolver( - manager_->GetHostResolver())); + resolver_.reset( + new net::SingleRequestHostResolver(manager_->GetHostResolver())); int net_result = resolver_->Resolve( - request_info, &address_list_, + request_info, + net::DEFAULT_PRIORITY, + &address_list_, base::Bind(&PepperTCPSocket::OnResolveCompleted, base::Unretained(this)), net::BoundNetLog()); if (net_result != net::ERR_IO_PENDING) @@ -141,16 +143,16 @@ void PepperTCPSocket::SSLHandshake( connection_state_ = SSL_HANDSHAKE_IN_PROGRESS; // TODO(raymes,rsleevi): Use trusted/untrusted certificates when connecting. - net::ClientSocketHandle* handle = new net::ClientSocketHandle(); - handle->set_socket(socket_.release()); + scoped_ptr<net::ClientSocketHandle> handle(new net::ClientSocketHandle()); + handle->SetSocket(socket_.Pass()); net::ClientSocketFactory* factory = net::ClientSocketFactory::GetDefaultFactory(); net::HostPortPair host_port_pair(server_name, server_port); net::SSLClientSocketContext ssl_context; ssl_context.cert_verifier = manager_->GetCertVerifier(); ssl_context.transport_security_state = manager_->GetTransportSecurityState(); - socket_.reset(factory->CreateSSLClientSocket( - handle, host_port_pair, manager_->ssl_config(), ssl_context)); + socket_ = factory->CreateSSLClientSocket( + handle.Pass(), host_port_pair, manager_->ssl_config(), ssl_context); if (!socket_) { LOG(WARNING) << "Failed to create an SSL client socket."; OnSSLHandshakeCompleted(net::ERR_UNEXPECTED); diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.h deleted file mode 100644 index 986afb015bb..00000000000 --- a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2012 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_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_ -#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/address_list.h" -#include "net/base/completion_callback.h" -#include "ppapi/c/pp_stdint.h" -#include "ppapi/c/ppb_tcp_socket.h" - -struct PP_NetAddress_Private; - -namespace ppapi { -class PPB_X509Certificate_Fields; -class SocketOptionData; -} - -namespace net { -class DrainableIOBuffer; -class IOBuffer; -class SingleRequestHostResolver; -class StreamSocket; -class X509Certificate; -} - -namespace content { -class PepperMessageFilter; - -// PepperTCPSocket is used by PepperMessageFilter to handle requests from -// the Pepper TCP socket API (PPB_TCPSocket and PPB_TCPSocket_Private). -class PepperTCPSocket { - public: - PepperTCPSocket(PepperMessageFilter* manager, - int32 routing_id, - uint32 plugin_dispatcher_id, - uint32 socket_id, - bool private_api); - - // Used for creation already connected sockets. Takes ownership of - // |socket|. - PepperTCPSocket(PepperMessageFilter* manager, - int32 routing_id, - uint32 plugin_dispatcher_id, - uint32 socket_id, - net::StreamSocket* socket, - bool private_api); - ~PepperTCPSocket(); - - int routing_id() { return routing_id_; } - bool private_api() const { return private_api_; } - - void Connect(const std::string& host, uint16_t port); - void ConnectWithNetAddress(const PP_NetAddress_Private& net_addr); - void SSLHandshake( - const std::string& server_name, - uint16_t server_port, - const std::vector<std::vector<char> >& trusted_certs, - const std::vector<std::vector<char> >& untrusted_certs); - void Read(int32 bytes_to_read); - void Write(const std::string& data); - void SetOption(PP_TCPSocket_Option name, - const ppapi::SocketOptionData& value); - - void SendConnectACKError(int32_t error); - - // Extracts the certificate field data from a |net::X509Certificate| into - // |PPB_X509Certificate_Fields|. - static bool GetCertificateFields(const net::X509Certificate& cert, - ppapi::PPB_X509Certificate_Fields* fields); - // Extracts the certificate field data from the DER representation of a - // certificate into |PPB_X509Certificate_Fields|. - static bool GetCertificateFields(const char* der, - uint32_t length, - ppapi::PPB_X509Certificate_Fields* fields); - - private: - enum ConnectionState { - // Before a connection is successfully established (including a previous - // connect request failed). - BEFORE_CONNECT, - // There is a connect request that is pending. - CONNECT_IN_PROGRESS, - // A connection has been successfully established. - CONNECTED, - // There is an SSL handshake request that is pending. - SSL_HANDSHAKE_IN_PROGRESS, - // An SSL connection has been successfully established. - SSL_CONNECTED, - // An SSL handshake has failed. - SSL_HANDSHAKE_FAILED - }; - - void StartConnect(const net::AddressList& addresses); - - void SendReadACKError(int32_t error); - void SendWriteACKError(int32_t error); - void SendSSLHandshakeACK(bool succeeded); - void SendSetOptionACK(int32_t result); - - void OnResolveCompleted(int net_result); - void OnConnectCompleted(int net_result); - void OnSSLHandshakeCompleted(int net_result); - void OnReadCompleted(int net_result); - void OnWriteCompleted(int net_result); - - bool IsConnected() const; - bool IsSsl() const; - - // Actually does a write from |write_buffer_|; possibly called many times for - // each |Write()|. - void DoWrite(); - - PepperMessageFilter* manager_; - int32 routing_id_; - uint32 plugin_dispatcher_id_; - uint32 socket_id_; - bool private_api_; - - ConnectionState connection_state_; - bool end_of_file_reached_; - - scoped_ptr<net::SingleRequestHostResolver> resolver_; - net::AddressList address_list_; - - scoped_ptr<net::StreamSocket> socket_; - - scoped_refptr<net::IOBuffer> read_buffer_; - - // |StreamSocket::Write()| may not always write the full buffer, but we would - // rather have our |Write()| do so whenever possible. To do this, we may have - // to call the former multiple times for each of the latter. This entails - // using a |DrainableIOBuffer|, which requires an underlying base |IOBuffer|. - scoped_refptr<net::IOBuffer> write_buffer_base_; - scoped_refptr<net::DrainableIOBuffer> write_buffer_; - - DISALLOW_COPY_AND_ASSIGN(PepperTCPSocket); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc new file mode 100644 index 00000000000..f943f39eb9e --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc @@ -0,0 +1,979 @@ +// Copyright 2013 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/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h" + +#include <cstring> + +#include "base/bind.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" +#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" +#include "content/browser/renderer_host/pepper/pepper_socket_utils.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/resource_context.h" +#include "content/public/common/socket_permission_request.h" +#include "net/base/address_family.h" +#include "net/base/host_port_pair.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/dns/single_request_host_resolver.h" +#include "net/socket/client_socket_factory.h" +#include "net/socket/client_socket_handle.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/tcp_client_socket.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/host/error_conversion.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/host/resource_host.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/tcp_socket_resource_base.h" +#include "ppapi/shared_impl/private/net_address_private_impl.h" + +using ppapi::NetAddressPrivateImpl; +using ppapi::host::NetErrorToPepperError; +using ppapi::proxy::TCPSocketResourceBase; +using ppapi::TCPSocketState; +using ppapi::TCPSocketVersion; + +namespace { + +size_t g_num_instances = 0; + +} // namespace + +namespace content { + +PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, + BrowserPpapiHostImpl* host, + PP_Instance instance, + TCPSocketVersion version) + : version_(version), + external_plugin_(host->external_plugin()), + render_process_id_(0), + render_view_id_(0), + ppapi_host_(host->GetPpapiHost()), + factory_(factory), + instance_(instance), + state_(TCPSocketState::INITIAL), + end_of_file_reached_(false), + bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress), + address_index_(0), + socket_(new net::TCPSocket(NULL, net::NetLog::Source())), + ssl_context_helper_(host->ssl_context_helper()), + pending_accept_(false) { + DCHECK(host); + ++g_num_instances; + if (!host->GetRenderViewIDsForInstance(instance, + &render_process_id_, + &render_view_id_)) { + NOTREACHED(); + } +} + +PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter( + BrowserPpapiHostImpl* host, + PP_Instance instance, + TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket) + : version_(version), + external_plugin_(host->external_plugin()), + render_process_id_(0), + render_view_id_(0), + ppapi_host_(host->GetPpapiHost()), + factory_(NULL), + instance_(instance), + state_(TCPSocketState::CONNECTED), + end_of_file_reached_(false), + bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress), + address_index_(0), + socket_(socket.Pass()), + ssl_context_helper_(host->ssl_context_helper()), + pending_accept_(false) { + DCHECK(host); + DCHECK_NE(version, ppapi::TCP_SOCKET_VERSION_1_0); + + ++g_num_instances; + if (!host->GetRenderViewIDsForInstance(instance, + &render_process_id_, + &render_view_id_)) { + NOTREACHED(); + } +} + +PepperTCPSocketMessageFilter::~PepperTCPSocketMessageFilter() { + if (socket_) + socket_->Close(); + if (ssl_socket_) + ssl_socket_->Disconnect(); + --g_num_instances; +} + +// static +size_t PepperTCPSocketMessageFilter::GetNumInstances() { + return g_num_instances; +} + +scoped_refptr<base::TaskRunner> +PepperTCPSocketMessageFilter::OverrideTaskRunnerForMessage( + const IPC::Message& message) { + switch (message.type()) { + case PpapiHostMsg_TCPSocket_Bind::ID: + case PpapiHostMsg_TCPSocket_Connect::ID: + case PpapiHostMsg_TCPSocket_ConnectWithNetAddress::ID: + case PpapiHostMsg_TCPSocket_Listen::ID: + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); + case PpapiHostMsg_TCPSocket_SSLHandshake::ID: + case PpapiHostMsg_TCPSocket_Read::ID: + case PpapiHostMsg_TCPSocket_Write::ID: + case PpapiHostMsg_TCPSocket_Accept::ID: + case PpapiHostMsg_TCPSocket_Close::ID: + case PpapiHostMsg_TCPSocket_SetOption::ID: + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); + } + return NULL; +} + +int32_t PepperTCPSocketMessageFilter::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + IPC_BEGIN_MESSAGE_MAP(PepperTCPSocketMessageFilter, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Bind, OnMsgBind) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Connect, OnMsgConnect) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_ConnectWithNetAddress, + OnMsgConnectWithNetAddress) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_SSLHandshake, OnMsgSSLHandshake) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Read, OnMsgRead) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Write, OnMsgWrite) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_Listen, OnMsgListen) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( + PpapiHostMsg_TCPSocket_Accept, OnMsgAccept) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( + PpapiHostMsg_TCPSocket_Close, OnMsgClose) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_TCPSocket_SetOption, OnMsgSetOption) + IPC_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgBind( + const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket v1.1 or above. + if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, false /* private_api */, NULL, render_process_id_, + render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + bind_input_addr_ = net_addr; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoBind, this, + context->MakeReplyMessageContext(), net_addr)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgConnect( + const ppapi::host::HostMessageContext* context, + const std::string& host, + uint16_t port) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket_Private. + if (!IsPrivateAPI()) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + SocketPermissionRequest request(SocketPermissionRequest::TCP_CONNECT, + host, + port); + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, true /* private_api */, &request, + render_process_id_, render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + RenderProcessHost* render_process_host = + RenderProcessHost::FromID(render_process_id_); + if (!render_process_host) + return PP_ERROR_FAILED; + BrowserContext* browser_context = render_process_host->GetBrowserContext(); + if (!browser_context || !browser_context->GetResourceContext()) + return PP_ERROR_FAILED; + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoConnect, this, + context->MakeReplyMessageContext(), + host, port, browser_context->GetResourceContext())); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgConnectWithNetAddress( + const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + content::SocketPermissionRequest request = + pepper_socket_utils::CreateSocketPermissionRequest( + content::SocketPermissionRequest::TCP_CONNECT, net_addr); + if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, IsPrivateAPI(), + &request, render_process_id_, + render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoConnectWithNetAddress, this, + context->MakeReplyMessageContext(), net_addr)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake( + const ppapi::host::HostMessageContext* context, + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Allow to do SSL handshake only if currently the socket has been connected + // and there isn't pending read or write. + if (!state_.IsValidTransition(TCPSocketState::SSL_CONNECT) || + read_buffer_.get() || write_buffer_base_.get() || write_buffer_.get()) { + return PP_ERROR_FAILED; + } + + // TODO(raymes,rsleevi): Use trusted/untrusted certificates when connecting. + net::IPEndPoint peer_address; + if (socket_->GetPeerAddress(&peer_address) != net::OK) + return PP_ERROR_FAILED; + + scoped_ptr<net::ClientSocketHandle> handle(new net::ClientSocketHandle()); + handle->SetSocket(make_scoped_ptr<net::StreamSocket>( + new net::TCPClientSocket(socket_.Pass(), peer_address))); + net::ClientSocketFactory* factory = + net::ClientSocketFactory::GetDefaultFactory(); + net::HostPortPair host_port_pair(server_name, server_port); + net::SSLClientSocketContext ssl_context; + ssl_context.cert_verifier = ssl_context_helper_->GetCertVerifier(); + ssl_context.transport_security_state = + ssl_context_helper_->GetTransportSecurityState(); + ssl_socket_ = factory->CreateSSLClientSocket( + handle.Pass(), host_port_pair, ssl_context_helper_->ssl_config(), + ssl_context); + if (!ssl_socket_) { + LOG(WARNING) << "Failed to create an SSL client socket."; + state_.CompletePendingTransition(false); + return PP_ERROR_FAILED; + } + + state_.SetPendingTransition(TCPSocketState::SSL_CONNECT); + + const ppapi::host::ReplyMessageContext reply_context( + context->MakeReplyMessageContext()); + int net_result = ssl_socket_->Connect( + base::Bind(&PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted, + base::Unretained(this), reply_context)); + if (net_result != net::ERR_IO_PENDING) + OnSSLHandshakeCompleted(reply_context, net_result); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgRead( + const ppapi::host::HostMessageContext* context, + int32_t bytes_to_read) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!state_.IsConnected() || end_of_file_reached_) + return PP_ERROR_FAILED; + if (read_buffer_.get()) + return PP_ERROR_INPROGRESS; + if (bytes_to_read <= 0 || + bytes_to_read > TCPSocketResourceBase::kMaxReadSize) { + return PP_ERROR_BADARGUMENT; + } + + ppapi::host::ReplyMessageContext reply_context( + context->MakeReplyMessageContext()); + read_buffer_ = new net::IOBuffer(bytes_to_read); + + int net_result = net::ERR_FAILED; + if (socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED); + net_result = socket_->Read( + read_buffer_.get(), + bytes_to_read, + base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted, + base::Unretained(this), reply_context)); + } else if (ssl_socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED); + net_result = ssl_socket_->Read( + read_buffer_.get(), + bytes_to_read, + base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted, + base::Unretained(this), reply_context)); + } + if (net_result != net::ERR_IO_PENDING) + OnReadCompleted(reply_context, net_result); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgWrite( + const ppapi::host::HostMessageContext* context, + const std::string& data) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsConnected()) + return PP_ERROR_FAILED; + if (write_buffer_base_.get() || write_buffer_.get()) + return PP_ERROR_INPROGRESS; + + size_t data_size = data.size(); + if (data_size == 0 || + data_size > static_cast<size_t>(TCPSocketResourceBase::kMaxWriteSize)) { + return PP_ERROR_BADARGUMENT; + } + + write_buffer_base_ = new net::IOBuffer(data_size); + memcpy(write_buffer_base_->data(), data.data(), data_size); + write_buffer_ = + new net::DrainableIOBuffer(write_buffer_base_.get(), data_size); + DoWrite(context->MakeReplyMessageContext()); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgListen( + const ppapi::host::HostMessageContext* context, + int32_t backlog) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // This is only supported by PPB_TCPSocket v1.1 or above. + if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + NOTREACHED(); + return PP_ERROR_NOACCESS; + } + + content::SocketPermissionRequest request = + pepper_socket_utils::CreateSocketPermissionRequest( + content::SocketPermissionRequest::TCP_LISTEN, bind_input_addr_); + if (!pepper_socket_utils::CanUseSocketAPIs( + external_plugin_, false /* private_api */, &request, + render_process_id_, render_view_id_)) { + return PP_ERROR_NOACCESS; + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PepperTCPSocketMessageFilter::DoListen, this, + context->MakeReplyMessageContext(), backlog)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgAccept( + const ppapi::host::HostMessageContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (pending_accept_) + return PP_ERROR_INPROGRESS; + if (state_.state() != TCPSocketState::LISTENING) + return PP_ERROR_FAILED; + + pending_accept_ = true; + ppapi::host::ReplyMessageContext reply_context( + context->MakeReplyMessageContext()); + int net_result = socket_->Accept( + &accepted_socket_, + &accepted_address_, + base::Bind(&PepperTCPSocketMessageFilter::OnAcceptCompleted, + base::Unretained(this), reply_context)); + if (net_result != net::ERR_IO_PENDING) + OnAcceptCompleted(reply_context, net_result); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgClose( + const ppapi::host::HostMessageContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (state_.state() == TCPSocketState::CLOSED) + return PP_OK; + + state_.DoTransition(TCPSocketState::CLOSE, true); + // Make sure we get no further callbacks from |socket_| or |ssl_socket_|. + if (socket_) { + socket_->Close(); + } else if (ssl_socket_) { + ssl_socket_->Disconnect(); + } + return PP_OK; +} + +int32_t PepperTCPSocketMessageFilter::OnMsgSetOption( + const ppapi::host::HostMessageContext* context, + PP_TCPSocket_Option name, + const ppapi::SocketOptionData& value) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + switch (name) { + case PP_TCPSOCKET_OPTION_NO_DELAY: { + if (state_.state() != TCPSocketState::CONNECTED) + return PP_ERROR_FAILED; + + bool boolean_value = false; + if (!value.GetBool(&boolean_value)) + return PP_ERROR_BADARGUMENT; + return socket_->SetNoDelay(boolean_value) ? PP_OK : PP_ERROR_FAILED; + } + case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE: + case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: { + if (state_.state() != TCPSocketState::CONNECTED) + return PP_ERROR_FAILED; + + int32_t integer_value = 0; + if (!value.GetInt32(&integer_value) || integer_value <= 0) + return PP_ERROR_BADARGUMENT; + + bool result = false; + if (name == PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE) { + if (integer_value > TCPSocketResourceBase::kMaxSendBufferSize) + return PP_ERROR_BADARGUMENT; + result = socket_->SetSendBufferSize(integer_value); + } else { + if (integer_value > TCPSocketResourceBase::kMaxReceiveBufferSize) + return PP_ERROR_BADARGUMENT; + result = socket_->SetReceiveBufferSize(integer_value); + } + return result ? PP_OK : PP_ERROR_FAILED; + } + default: { + NOTREACHED(); + return PP_ERROR_BADARGUMENT; + } + } +} + +void PepperTCPSocketMessageFilter::DoBind( + const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (state_.IsPending(TCPSocketState::BIND)) { + SendBindError(context, PP_ERROR_INPROGRESS); + return; + } + if (!state_.IsValidTransition(TCPSocketState::BIND)) { + SendBindError(context, PP_ERROR_FAILED); + return; + } + + int pp_result = PP_OK; + do { + net::IPAddressNumber address; + int port; + if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address, + &port)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + net::IPEndPoint bind_addr(address, port); + + DCHECK(!socket_->IsValid()); + pp_result = NetErrorToPepperError(socket_->Open(bind_addr.GetFamily())); + if (pp_result != PP_OK) + break; + + pp_result = NetErrorToPepperError(socket_->SetDefaultOptionsForServer()); + if (pp_result != PP_OK) + break; + + pp_result = NetErrorToPepperError(socket_->Bind(bind_addr)); + if (pp_result != PP_OK) + break; + + net::IPEndPoint ip_end_point_local; + pp_result = NetErrorToPepperError( + socket_->GetLocalAddress(&ip_end_point_local)); + if (pp_result != PP_OK) + break; + + PP_NetAddress_Private local_addr = + NetAddressPrivateImpl::kInvalidNetAddress; + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + + SendBindReply(context, PP_OK, local_addr); + state_.DoTransition(TCPSocketState::BIND, true); + return; + } while (false); + if (socket_->IsValid()) + socket_->Close(); + SendBindError(context, pp_result); + state_.DoTransition(TCPSocketState::BIND, false); +} + +void PepperTCPSocketMessageFilter::DoConnect( + const ppapi::host::ReplyMessageContext& context, + const std::string& host, + uint16_t port, + ResourceContext* resource_context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) { + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + state_.SetPendingTransition(TCPSocketState::CONNECT); + address_index_ = 0; + address_list_.clear(); + net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port)); + resolver_.reset(new net::SingleRequestHostResolver( + resource_context->GetHostResolver())); + int net_result = resolver_->Resolve( + request_info, + net::DEFAULT_PRIORITY, + &address_list_, + base::Bind(&PepperTCPSocketMessageFilter::OnResolveCompleted, + base::Unretained(this), context), + net::BoundNetLog()); + if (net_result != net::ERR_IO_PENDING) + OnResolveCompleted(context, net_result); +} + +void PepperTCPSocketMessageFilter::DoConnectWithNetAddress( + const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsValidTransition(TCPSocketState::CONNECT)) { + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + state_.SetPendingTransition(TCPSocketState::CONNECT); + + net::IPAddressNumber address; + int port; + if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address, + &port)) { + state_.CompletePendingTransition(false); + SendConnectError(context, PP_ERROR_ADDRESS_INVALID); + return; + } + + // Copy the single IPEndPoint to address_list_. + address_index_ = 0; + address_list_.clear(); + address_list_.push_back(net::IPEndPoint(address, port)); + StartConnect(context); +} + +void PepperTCPSocketMessageFilter::DoWrite( + const ppapi::host::ReplyMessageContext& context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(write_buffer_base_.get()); + DCHECK(write_buffer_.get()); + DCHECK_GT(write_buffer_->BytesRemaining(), 0); + DCHECK(state_.IsConnected()); + + int net_result = net::ERR_FAILED; + if (socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED); + net_result = socket_->Write( + write_buffer_.get(), + write_buffer_->BytesRemaining(), + base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted, + base::Unretained(this), context)); + } else if (ssl_socket_) { + DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED); + net_result = ssl_socket_->Write( + write_buffer_.get(), + write_buffer_->BytesRemaining(), + base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted, + base::Unretained(this), context)); + } + if (net_result != net::ERR_IO_PENDING) + OnWriteCompleted(context, net_result); +} + +void PepperTCPSocketMessageFilter::DoListen( + const ppapi::host::ReplyMessageContext& context, + int32_t backlog) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (state_.IsPending(TCPSocketState::LISTEN)) { + SendListenReply(context, PP_ERROR_INPROGRESS); + return; + } + if (!state_.IsValidTransition(TCPSocketState::LISTEN)) { + SendListenReply(context, PP_ERROR_FAILED); + return; + } + + int32_t pp_result = NetErrorToPepperError(socket_->Listen(backlog)); + SendListenReply(context, pp_result); + state_.DoTransition(TCPSocketState::LISTEN, pp_result == PP_OK); +} + +void PepperTCPSocketMessageFilter::OnResolveCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsPending(TCPSocketState::CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + if (net_result != net::OK) { + SendConnectError(context, NetErrorToPepperError(net_result)); + state_.CompletePendingTransition(false); + return; + } + + StartConnect(context); +} + +void PepperTCPSocketMessageFilter::StartConnect( + const ppapi::host::ReplyMessageContext& context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(state_.IsPending(TCPSocketState::CONNECT)); + DCHECK_LT(address_index_, address_list_.size()); + + int net_result = net::OK; + if (!socket_->IsValid()) + net_result = socket_->Open(address_list_[address_index_].GetFamily()); + + if (net_result == net::OK) { + net_result = socket_->Connect( + address_list_[address_index_], + base::Bind(&PepperTCPSocketMessageFilter::OnConnectCompleted, + base::Unretained(this), context)); + } + if (net_result != net::ERR_IO_PENDING) + OnConnectCompleted(context, net_result); +} + +void PepperTCPSocketMessageFilter::OnConnectCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsPending(TCPSocketState::CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); + SendConnectError(context, PP_ERROR_FAILED); + return; + } + + int32_t pp_result = NetErrorToPepperError(net_result); + do { + if (pp_result != PP_OK) + break; + + net::IPEndPoint ip_end_point_local; + net::IPEndPoint ip_end_point_remote; + pp_result = NetErrorToPepperError( + socket_->GetLocalAddress(&ip_end_point_local)); + if (pp_result != PP_OK) + break; + pp_result = NetErrorToPepperError( + socket_->GetPeerAddress(&ip_end_point_remote)); + if (pp_result != PP_OK) + break; + + PP_NetAddress_Private local_addr = + NetAddressPrivateImpl::kInvalidNetAddress; + PP_NetAddress_Private remote_addr = + NetAddressPrivateImpl::kInvalidNetAddress; + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_remote.address(), + ip_end_point_remote.port(), + &remote_addr)) { + pp_result = PP_ERROR_ADDRESS_INVALID; + break; + } + + socket_->SetDefaultOptionsForClient(); + SendConnectReply(context, PP_OK, local_addr, remote_addr); + state_.CompletePendingTransition(true); + return; + } while (false); + + if (version_ == ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) { + DCHECK_EQ(1u, address_list_.size()); + + SendConnectError(context, pp_result); + state_.CompletePendingTransition(false); + } else { + // We have to recreate |socket_| because it doesn't allow a second connect + // attempt. We won't lose any state such as bound address or set options, + // because in the private or v1.0 API, connect must be the first operation. + socket_.reset(new net::TCPSocket(NULL, net::NetLog::Source())); + + if (address_index_ + 1 < address_list_.size()) { + DCHECK_EQ(version_, ppapi::TCP_SOCKET_VERSION_PRIVATE); + address_index_++; + StartConnect(context); + } else { + SendConnectError(context, pp_result); + // In order to maintain backward compatibility, allow further attempts to + // connect the socket. + state_ = TCPSocketState(TCPSocketState::INITIAL); + } + } +} + +void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!state_.IsPending(TCPSocketState::SSL_CONNECT)) { + DCHECK(state_.state() == TCPSocketState::CLOSED); + SendSSLHandshakeReply(context, PP_ERROR_FAILED); + return; + } + + SendSSLHandshakeReply(context, NetErrorToPepperError(net_result)); + state_.CompletePendingTransition(net_result == net::OK); +} + +void PepperTCPSocketMessageFilter::OnReadCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(read_buffer_.get()); + + if (net_result > 0) { + SendReadReply(context, + PP_OK, + std::string(read_buffer_->data(), net_result)); + } else if (net_result == 0) { + end_of_file_reached_ = true; + SendReadReply(context, PP_OK, std::string()); + } else { + SendReadError(context, NetErrorToPepperError(net_result)); + } + read_buffer_ = NULL; +} + +void PepperTCPSocketMessageFilter::OnWriteCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(write_buffer_base_.get()); + DCHECK(write_buffer_.get()); + + // Note: For partial writes of 0 bytes, don't continue writing to avoid a + // likely infinite loop. + if (net_result > 0) { + write_buffer_->DidConsume(net_result); + if (write_buffer_->BytesRemaining() > 0 && state_.IsConnected()) { + DoWrite(context); + return; + } + } + + if (net_result >= 0) + SendWriteReply(context, write_buffer_->BytesConsumed()); + else + SendWriteReply(context, NetErrorToPepperError(net_result)); + + write_buffer_ = NULL; + write_buffer_base_ = NULL; +} + +void PepperTCPSocketMessageFilter::OnAcceptCompleted( + const ppapi::host::ReplyMessageContext& context, + int net_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(pending_accept_); + + pending_accept_ = false; + + if (net_result != net::OK) { + SendAcceptError(context, NetErrorToPepperError(net_result)); + return; + } + + DCHECK(accepted_socket_.get()); + + net::IPEndPoint ip_end_point_local; + PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress; + PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress; + + int32_t pp_result = + NetErrorToPepperError(accepted_socket_->GetLocalAddress( + &ip_end_point_local)); + if (pp_result != PP_OK) { + SendAcceptError(context, pp_result); + return; + } + if (!NetAddressPrivateImpl::IPEndPointToNetAddress( + ip_end_point_local.address(), + ip_end_point_local.port(), + &local_addr) || + !NetAddressPrivateImpl::IPEndPointToNetAddress( + accepted_address_.address(), + accepted_address_.port(), + &remote_addr)) { + SendAcceptError(context, PP_ERROR_ADDRESS_INVALID); + return; + } + + // |factory_| is guaranteed to be non-NULL here. Only those instances created + // in CONNECTED state have a NULL |factory_|, while getting here requires + // LISTENING state. + scoped_ptr<ppapi::host::ResourceHost> host = + factory_->CreateAcceptedTCPSocket( + instance_, version_, accepted_socket_.Pass()); + if (!host) { + SendAcceptError(context, PP_ERROR_NOSPACE); + return; + } + int pending_host_id = ppapi_host_->AddPendingResourceHost(host.Pass()); + if (pending_host_id) + SendAcceptReply(context, PP_OK, pending_host_id, local_addr, remote_addr); + else + SendAcceptError(context, PP_ERROR_NOSPACE); +} + +void PepperTCPSocketMessageFilter::SendBindReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_BindReply(local_addr)); +} + +void PepperTCPSocketMessageFilter::SendBindError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendBindReply(context, pp_error, NetAddressPrivateImpl::kInvalidNetAddress); +} + +void PepperTCPSocketMessageFilter::SendConnectReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, + PpapiPluginMsg_TCPSocket_ConnectReply(local_addr, remote_addr)); +} + +void PepperTCPSocketMessageFilter::SendConnectError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendConnectReply(context, + pp_error, + NetAddressPrivateImpl::kInvalidNetAddress, + NetAddressPrivateImpl::kInvalidNetAddress); +} + +void PepperTCPSocketMessageFilter::SendSSLHandshakeReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + ppapi::PPB_X509Certificate_Fields certificate_fields; + if (pp_result == PP_OK) { + // Our socket is guaranteed to be an SSL socket if we get here. + net::SSLInfo ssl_info; + ssl_socket_->GetSSLInfo(&ssl_info); + if (ssl_info.cert.get()) { + pepper_socket_utils::GetCertificateFields(*ssl_info.cert.get(), + &certificate_fields); + } + } + SendReply(reply_context, + PpapiPluginMsg_TCPSocket_SSLHandshakeReply(certificate_fields)); +} + +void PepperTCPSocketMessageFilter::SendReadReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const std::string& data) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_ReadReply(data)); +} + +void PepperTCPSocketMessageFilter::SendReadError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendReadReply(context, pp_error, std::string()); +} + +void PepperTCPSocketMessageFilter::SendWriteReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_WriteReply()); +} + +void PepperTCPSocketMessageFilter::SendListenReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, PpapiPluginMsg_TCPSocket_ListenReply()); +} + +void PepperTCPSocketMessageFilter::SendAcceptReply( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + ppapi::host::ReplyMessageContext reply_context(context); + reply_context.params.set_result(pp_result); + SendReply(reply_context, + PpapiPluginMsg_TCPSocket_AcceptReply( + pending_host_id, local_addr, remote_addr)); +} + +void PepperTCPSocketMessageFilter::SendAcceptError( + const ppapi::host::ReplyMessageContext& context, + int32_t pp_error) { + SendAcceptReply(context, pp_error, 0, + NetAddressPrivateImpl::kInvalidNetAddress, + NetAddressPrivateImpl::kInvalidNetAddress); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h new file mode 100644 index 00000000000..fb34b988d3c --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h @@ -0,0 +1,223 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/browser/renderer_host/pepper/ssl_context_helper.h" +#include "content/common/content_export.h" +#include "net/base/address_list.h" +#include "net/base/ip_endpoint.h" +#include "net/socket/tcp_socket.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/ppb_tcp_socket.h" +#include "ppapi/c/private/ppb_net_address_private.h" +#include "ppapi/host/resource_message_filter.h" +#include "ppapi/shared_impl/ppb_tcp_socket_shared.h" + +namespace net { +enum AddressFamily; +class DrainableIOBuffer; +class IOBuffer; +class SingleRequestHostResolver; +class SSLClientSocket; +} + +namespace ppapi { +class SocketOptionData; + +namespace host { +class PpapiHost; +struct ReplyMessageContext; +} +} + +namespace content { + +class BrowserPpapiHostImpl; +class ContentBrowserPepperHostFactory; +class ResourceContext; + +class CONTENT_EXPORT PepperTCPSocketMessageFilter + : public ppapi::host::ResourceMessageFilter { + public: + PepperTCPSocketMessageFilter( + ContentBrowserPepperHostFactory* factory, + BrowserPpapiHostImpl* host, + PP_Instance instance, + ppapi::TCPSocketVersion version); + + // Used for creating already connected sockets. + PepperTCPSocketMessageFilter( + BrowserPpapiHostImpl* host, + PP_Instance instance, + ppapi::TCPSocketVersion version, + scoped_ptr<net::TCPSocket> socket); + + static size_t GetNumInstances(); + + private: + virtual ~PepperTCPSocketMessageFilter(); + + // ppapi::host::ResourceMessageFilter overrides. + virtual scoped_refptr<base::TaskRunner> OverrideTaskRunnerForMessage( + const IPC::Message& message) OVERRIDE; + virtual int32_t OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) OVERRIDE; + + int32_t OnMsgBind(const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr); + int32_t OnMsgConnect(const ppapi::host::HostMessageContext* context, + const std::string& host, + uint16_t port); + int32_t OnMsgConnectWithNetAddress( + const ppapi::host::HostMessageContext* context, + const PP_NetAddress_Private& net_addr); + int32_t OnMsgSSLHandshake( + const ppapi::host::HostMessageContext* context, + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs); + int32_t OnMsgRead(const ppapi::host::HostMessageContext* context, + int32_t bytes_to_read); + int32_t OnMsgWrite(const ppapi::host::HostMessageContext* context, + const std::string& data); + int32_t OnMsgListen(const ppapi::host::HostMessageContext* context, + int32_t backlog); + int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context); + int32_t OnMsgClose(const ppapi::host::HostMessageContext* context); + int32_t OnMsgSetOption(const ppapi::host::HostMessageContext* context, + PP_TCPSocket_Option name, + const ppapi::SocketOptionData& value); + + void DoBind(const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr); + void DoConnect(const ppapi::host::ReplyMessageContext& context, + const std::string& host, + uint16_t port, + ResourceContext* resource_context); + void DoConnectWithNetAddress( + const ppapi::host::ReplyMessageContext& context, + const PP_NetAddress_Private& net_addr); + void DoWrite(const ppapi::host::ReplyMessageContext& context); + void DoListen(const ppapi::host::ReplyMessageContext& context, + int32_t backlog); + + void OnResolveCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void StartConnect(const ppapi::host::ReplyMessageContext& context); + + void OnConnectCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnSSLHandshakeCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnReadCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnWriteCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + void OnAcceptCompleted(const ppapi::host::ReplyMessageContext& context, + int net_result); + + void SendBindReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr); + void SendBindError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + void SendConnectReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); + void SendConnectError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + void SendSSLHandshakeReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result); + void SendReadReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + const std::string& data); + void SendReadError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + void SendWriteReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result); + void SendListenReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result); + void SendAcceptReply(const ppapi::host::ReplyMessageContext& context, + int32_t pp_result, + int pending_host_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); + void SendAcceptError(const ppapi::host::ReplyMessageContext& context, + int32_t pp_error); + + bool IsPrivateAPI() const { + return version_ == ppapi::TCP_SOCKET_VERSION_PRIVATE; + } + + // The following fields are used on both the UI and IO thread. + const ppapi::TCPSocketVersion version_; + + // The following fields are used only on the UI thread. + const bool external_plugin_; + + int render_process_id_; + int render_view_id_; + + // The following fields are used only on the IO thread. + // Non-owning ptr. + ppapi::host::PpapiHost* ppapi_host_; + // Non-owning ptr. + ContentBrowserPepperHostFactory* factory_; + PP_Instance instance_; + + ppapi::TCPSocketState state_; + bool end_of_file_reached_; + + // This is the address requested to bind. Please note that this is not the + // bound address. For example, |bind_input_addr_| may have port set to 0. + // It is used to check permission for listening. + PP_NetAddress_Private bind_input_addr_; + + scoped_ptr<net::SingleRequestHostResolver> resolver_; + + // |address_list_| may store multiple addresses when + // PPB_TCPSocket_Private.Connect() is used, which involves name resolution. + // In that case, we will try each address in the list until a connection is + // successfully established. + net::AddressList address_list_; + // Where we are in the above list. + size_t address_index_; + + // Non-null unless an SSL connection is requested. + scoped_ptr<net::TCPSocket> socket_; + // Non-null if an SSL connection is requested. + scoped_ptr<net::SSLClientSocket> ssl_socket_; + + scoped_refptr<net::IOBuffer> read_buffer_; + + // TCPSocket::Write() may not always write the full buffer, but we would + // rather have our DoWrite() do so whenever possible. To do this, we may have + // to call the former multiple times for each of the latter. This entails + // using a DrainableIOBuffer, which requires an underlying base IOBuffer. + scoped_refptr<net::IOBuffer> write_buffer_base_; + scoped_refptr<net::DrainableIOBuffer> write_buffer_; + scoped_refptr<SSLContextHelper> ssl_context_helper_; + + bool pending_accept_; + scoped_ptr<net::TCPSocket> accepted_socket_; + net::IPEndPoint accepted_address_; + + DISALLOW_COPY_AND_ASSIGN(PepperTCPSocketMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_ diff --git a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc index 7453fd2886a..3127ed278fd 100644 --- a/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc +++ b/chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc @@ -28,8 +28,8 @@ #include "ppapi/shared_impl/private/net_address_private_impl.h" #include "ppapi/shared_impl/socket_option_data.h" -using ppapi::host::NetErrorToPepperError; using ppapi::NetAddressPrivateImpl; +using ppapi::host::NetErrorToPepperError; namespace { @@ -173,7 +173,7 @@ int32_t PepperUDPSocketMessageFilter::OnMsgBind( pepper_socket_utils::CreateSocketPermissionRequest( SocketPermissionRequest::UDP_BIND, addr); if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, render_process_id_, + &request, render_process_id_, render_view_id_)) { return PP_ERROR_NOACCESS; } @@ -236,7 +236,7 @@ int32_t PepperUDPSocketMessageFilter::OnMsgSendTo( pepper_socket_utils::CreateSocketPermissionRequest( SocketPermissionRequest::UDP_SEND_TO, addr); if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_, - request, render_process_id_, + &request, render_process_id_, render_view_id_)) { return PP_ERROR_NOACCESS; } diff --git a/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc new file mode 100644 index 00000000000..3b92e6d0c16 --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.cc @@ -0,0 +1,30 @@ +// Copyright 2013 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/browser/renderer_host/pepper/ssl_context_helper.h" + +#include "net/cert/cert_verifier.h" +#include "net/http/transport_security_state.h" + +namespace content { + +SSLContextHelper::SSLContextHelper() { +} + +SSLContextHelper::~SSLContextHelper() { +} + +net::CertVerifier* SSLContextHelper::GetCertVerifier() { + if (!cert_verifier_) + cert_verifier_.reset(net::CertVerifier::CreateDefault()); + return cert_verifier_.get(); +} + +net::TransportSecurityState* SSLContextHelper::GetTransportSecurityState() { + if (!transport_security_state_) + transport_security_state_.reset(new net::TransportSecurityState()); + return transport_security_state_.get(); +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/pepper/ssl_context_helper.h b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.h new file mode 100644 index 00000000000..f1da2a6ee1d --- /dev/null +++ b/chromium/content/browser/renderer_host/pepper/ssl_context_helper.h @@ -0,0 +1,48 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "net/ssl/ssl_config_service.h" + +namespace net { +class CertVerifier; +class TransportSecurityState; +} + +namespace content { + +class SSLContextHelper : public base::RefCounted<SSLContextHelper> { + public: + SSLContextHelper(); + + net::CertVerifier* GetCertVerifier(); + net::TransportSecurityState* GetTransportSecurityState(); + const net::SSLConfig& ssl_config() { return ssl_config_; } + + private: + friend class base::RefCounted<SSLContextHelper>; + + ~SSLContextHelper(); + + // This is lazily created. Users should use GetCertVerifier to retrieve it. + scoped_ptr<net::CertVerifier> cert_verifier_; + // This is lazily created. Users should use GetTransportSecurityState to + // retrieve it. + scoped_ptr<net::TransportSecurityState> transport_security_state_; + + // The default SSL configuration settings are used, as opposed to Chrome's SSL + // settings. + net::SSLConfig ssl_config_; + + DISALLOW_COPY_AND_ASSIGN(SSLContextHelper); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_ diff --git a/chromium/content/browser/renderer_host/render_frame_host_impl.cc b/chromium/content/browser/renderer_host/render_frame_host_impl.cc index ea511d0b8c3..ec621c10dca 100644 --- a/chromium/content/browser/renderer_host/render_frame_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_frame_host_impl.cc @@ -4,31 +4,64 @@ #include "content/browser/renderer_host/render_frame_host_impl.h" +#include "base/containers/hash_tables.h" +#include "base/lazy_instance.h" #include "content/browser/renderer_host/render_view_host_impl.h" namespace content { +// The (process id, routing id) pair that identifies one RenderFrame. +typedef std::pair<int32, int32> RenderFrameHostID; +typedef base::hash_map<RenderFrameHostID, RenderFrameHostImpl*> + RoutingIDFrameMap; +static base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map = + LAZY_INSTANCE_INITIALIZER; + +// static +RenderFrameHostImpl* RenderFrameHostImpl::FromID( + int process_id, int routing_id) { + RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer(); + RoutingIDFrameMap::iterator it = frames->find( + RenderFrameHostID(process_id, routing_id)); + return it == frames->end() ? NULL : it->second; +} + RenderFrameHostImpl::RenderFrameHostImpl( RenderViewHostImpl* render_view_host, int routing_id, - bool swapped_out) + bool is_swapped_out) : render_view_host_(render_view_host), - routing_id_(routing_id) { + routing_id_(routing_id), + is_swapped_out_(is_swapped_out) { + GetProcess()->AddRoute(routing_id_, this); + g_routing_id_frame_map.Get().insert(std::make_pair( + RenderFrameHostID(GetProcess()->GetID(), routing_id_), + this)); } RenderFrameHostImpl::~RenderFrameHostImpl() { + GetProcess()->RemoveRoute(routing_id_); + g_routing_id_frame_map.Get().erase( + RenderFrameHostID(GetProcess()->GetID(), routing_id_)); + } bool RenderFrameHostImpl::Send(IPC::Message* message) { - // Use the RenderViewHost object to send the message. It inherits it from - // RenderWidgetHost, which ultimately uses the current process's |Send|. - return render_view_host_->Send(message); + return GetProcess()->Send(message); } bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message &msg) { - // Pass the message up to the RenderViewHost, until we have enough - // infrastructure to start processing messages in this object. - return render_view_host_->OnMessageReceived(msg); + return false; +} + +void RenderFrameHostImpl::Init() { + GetProcess()->ResumeRequestsForView(routing_id()); +} + +RenderProcessHost* RenderFrameHostImpl::GetProcess() const { + // TODO(ajwong): This should return its own process once cross-process + // subframe navigations are supported. + return render_view_host_->GetProcess(); } } // namespace content diff --git a/chromium/content/browser/renderer_host/render_frame_host_impl.h b/chromium/content/browser/renderer_host/render_frame_host_impl.h index bb94c8e63c2..78df3331efd 100644 --- a/chromium/content/browser/renderer_host/render_frame_host_impl.h +++ b/chromium/content/browser/renderer_host/render_frame_host_impl.h @@ -10,14 +10,16 @@ namespace content { +class RenderProcessHost; class RenderViewHostImpl; class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost { public: - RenderFrameHostImpl( - RenderViewHostImpl* render_view_host, - int routing_id, - bool swapped_out); + static RenderFrameHostImpl* FromID(int process_id, int routing_id); + + RenderFrameHostImpl(RenderViewHostImpl* render_view_host, + int routing_id, + bool is_swapped_out); virtual ~RenderFrameHostImpl(); // IPC::Sender @@ -26,12 +28,16 @@ class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost { // IPC::Listener virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; - int routing_id() { return routing_id_; } + void Init(); + RenderProcessHost* GetProcess() const; + int routing_id() const { return routing_id_; } private: - RenderViewHostImpl* render_view_host_; + bool is_swapped_out() { return is_swapped_out_; } + RenderViewHostImpl* render_view_host_; // Not owned. Outlives this object. int routing_id_; + bool is_swapped_out_; DISALLOW_COPY_AND_ASSIGN(RenderFrameHostImpl); }; diff --git a/chromium/content/browser/renderer_host/render_process_host_browsertest.cc b/chromium/content/browser/renderer_host/render_process_host_browsertest.cc index 9ee8036e044..de64924d717 100644 --- a/chromium/content/browser/renderer_host/render_process_host_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_process_host_browsertest.cc @@ -8,7 +8,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/test/test_notification_tracker.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" diff --git a/chromium/content/browser/renderer_host/render_process_host_impl.cc b/chromium/content/browser/renderer_host/render_process_host_impl.cc index 2dd784a4e69..73cb5e11f9c 100644 --- a/chromium/content/browser/renderer_host/render_process_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_process_host_impl.cc @@ -52,6 +52,7 @@ #include "content/browser/fileapi/chrome_blob_storage_context.h" #include "content/browser/fileapi/fileapi_message_filter.h" #include "content/browser/geolocation/geolocation_dispatcher_host.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/gpu/shader_disk_cache.h" @@ -60,6 +61,7 @@ #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" #include "content/browser/loader/resource_message_filter.h" #include "content/browser/loader/resource_scheduler_filter.h" +#include "content/browser/media/android/browser_demuxer_android.h" #include "content/browser/media/media_internals.h" #include "content/browser/mime_registry_message_filter.h" #include "content/browser/plugin_service_impl.h" @@ -91,6 +93,8 @@ #include "content/browser/renderer_host/socket_stream_dispatcher_host.h" #include "content/browser/renderer_host/text_input_client_message_filter.h" #include "content/browser/resolve_proxy_msg_helper.h" +#include "content/browser/service_worker/service_worker_context.h" +#include "content/browser/service_worker/service_worker_dispatcher_host.h" #include "content/browser/speech/input_tag_speech_dispatcher_host.h" #include "content/browser/speech/speech_recognition_dispatcher_host.h" #include "content/browser/storage_partition_impl.h" @@ -111,6 +115,7 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host_factory.h" #include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/content_constants.h" @@ -118,8 +123,6 @@ #include "content/public/common/process_type.h" #include "content/public/common/result_codes.h" #include "content/public/common/url_constants.h" -#include "content/renderer/render_process_impl.h" -#include "content/renderer/render_thread_impl.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_logging.h" @@ -129,6 +132,7 @@ #include "net/url_request/url_request_context_getter.h" #include "ppapi/shared_impl/ppapi_switches.h" #include "ui/base/ui_base_switches.h" +#include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" #include "webkit/browser/fileapi/sandbox_file_system_backend.h" #include "webkit/common/resource_type.h" @@ -157,8 +161,6 @@ static const char* kSiteProcessMapKeyName = "content_site_process_map"; namespace content { namespace { -base::MessageLoop* g_in_process_thread; - void CacheShaderInfo(int32 id, base::FilePath path) { ShaderCacheFactory::GetInstance()->SetCacheInfo(id, path); } @@ -167,88 +169,30 @@ void RemoveShaderInfo(int32 id) { ShaderCacheFactory::GetInstance()->RemoveCacheInfo(id); } -} // namespace - -#if !defined(CHROME_MULTIPLE_DLL) - -// This class creates the IO thread for the renderer when running in -// single-process mode. It's not used in multi-process mode. -class RendererMainThread : public base::Thread { - public: - explicit RendererMainThread(const std::string& channel_id) - : Thread("Chrome_InProcRendererThread"), - channel_id_(channel_id) { - } - - virtual ~RendererMainThread() { - Stop(); - } - - protected: - virtual void Init() OVERRIDE { - render_process_.reset(new RenderProcessImpl()); - new RenderThreadImpl(channel_id_); - g_in_process_thread = message_loop(); - } - - virtual void CleanUp() OVERRIDE { - g_in_process_thread = NULL; - render_process_.reset(); - - // It's a little lame to manually set this flag. But the single process - // RendererThread will receive the WM_QUIT. We don't need to assert on - // this thread, so just force the flag manually. - // If we want to avoid this, we could create the InProcRendererThread - // directly with _beginthreadex() rather than using the Thread class. - // We used to set this flag in the Init function above. However there - // other threads like WebThread which are created by this thread - // which resets this flag. Please see Thread::StartWithOptions. Setting - // this flag to true in Cleanup works around these problems. - SetThreadWasQuitProperly(true); - } - - private: - std::string channel_id_; - scoped_ptr<RenderProcess> render_process_; - - DISALLOW_COPY_AND_ASSIGN(RendererMainThread); -}; - -#endif - -namespace { - -// Helper class that we pass to ResourceMessageFilter so that it can find the -// right net::URLRequestContext for a request. -class RendererURLRequestContextSelector - : public ResourceMessageFilter::URLRequestContextSelector { - public: - RendererURLRequestContextSelector(BrowserContext* browser_context, - int render_child_id) - : request_context_(browser_context->GetRequestContextForRenderProcess( - render_child_id)), - media_request_context_( - browser_context->GetMediaRequestContextForRenderProcess( - render_child_id)) { - } - - virtual net::URLRequestContext* GetRequestContext( - ResourceType::Type resource_type) OVERRIDE { - net::URLRequestContextGetter* request_context = request_context_.get(); - // If the request has resource type of ResourceType::MEDIA, we use a request - // context specific to media for handling it because these resources have - // specific needs for caching. - if (resource_type == ResourceType::MEDIA) - request_context = media_request_context_.get(); - return request_context->GetURLRequestContext(); - } - - private: - virtual ~RendererURLRequestContextSelector() {} +net::URLRequestContext* GetRequestContext( + scoped_refptr<net::URLRequestContextGetter> request_context, + scoped_refptr<net::URLRequestContextGetter> media_request_context, + ResourceType::Type resource_type) { + // If the request has resource type of ResourceType::MEDIA, we use a request + // context specific to media for handling it because these resources have + // specific needs for caching. + if (resource_type == ResourceType::MEDIA) + return media_request_context->GetURLRequestContext(); + return request_context->GetURLRequestContext(); +} - scoped_refptr<net::URLRequestContextGetter> request_context_; - scoped_refptr<net::URLRequestContextGetter> media_request_context_; -}; +void GetContexts( + ResourceContext* resource_context, + scoped_refptr<net::URLRequestContextGetter> request_context, + scoped_refptr<net::URLRequestContextGetter> media_request_context, + const ResourceHostMsg_Request& request, + ResourceContext** resource_context_out, + net::URLRequestContext** request_context_out) { + *resource_context_out = resource_context; + *request_context_out = + GetRequestContext(request_context, media_request_context, + request.resource_type); +} // the global list of all renderer processes base::LazyInstance<IDMap<RenderProcessHost> >::Leaky @@ -339,6 +283,20 @@ class RendererSandboxedProcessLauncherDelegate } // namespace +RendererMainThreadFactoryFunction g_renderer_main_thread_factory = NULL; + +void RenderProcessHost::RegisterRendererMainThreadFactory( + RendererMainThreadFactoryFunction create) { + g_renderer_main_thread_factory = create; +} + +base::MessageLoop* g_in_process_thread; + +base::MessageLoop* + RenderProcessHostImpl::GetInProcessRendererThreadForTesting() { + return g_in_process_thread; +} + // Stores the maximum number of renderer processes the content module can // create. static size_t g_max_renderer_count_override = 0; @@ -511,15 +469,14 @@ bool RenderProcessHostImpl::Init() { CreateMessageFilters(); // Single-process mode not supported in multiple-dll mode currently. -#if !defined(CHROME_MULTIPLE_DLL) - if (run_renderer_in_process()) { + if (run_renderer_in_process() && g_renderer_main_thread_factory) { // Crank up a thread and run the initialization there. With the way that // messages flow between the browser and renderer, this thread is required // to prevent a deadlock in single-process mode. Since the primordial // thread in the renderer process runs the WebKit code and can sometimes // make blocking calls to the UI thread (i.e. this thread), they need to run // on separate threads. - in_process_renderer_.reset(new RendererMainThread(channel_id)); + in_process_renderer_.reset(g_renderer_main_thread_factory(channel_id)); base::Thread::Options options; #if defined(OS_WIN) && !defined(OS_MACOSX) @@ -532,10 +489,10 @@ bool RenderProcessHostImpl::Init() { #endif in_process_renderer_->StartWithOptions(options); + g_in_process_thread = in_process_renderer_->message_loop(); + OnProcessLaunched(); // Fake a callback that the process is ready. - } else -#endif // !CHROME_MULTIPLE_DLL - { + } else { // Build command line for renderer. We call AppendRendererCommandLine() // first so the process type argument will appear first. CommandLine* cmd_line = new CommandLine(renderer_path); @@ -552,7 +509,7 @@ bool RenderProcessHostImpl::Init() { new RendererSandboxedProcessLauncherDelegate, #elif defined(OS_POSIX) renderer_prefix.empty(), - base::EnvironmentVector(), + base::EnvironmentMap(), channel_->TakeClientFileDescriptor(), #endif cmd_line, @@ -604,12 +561,21 @@ void RenderProcessHostImpl::CreateMessageFilters() { BrowserContext* browser_context = GetBrowserContext(); ResourceContext* resource_context = browser_context->GetResourceContext(); + scoped_refptr<net::URLRequestContextGetter> request_context( + browser_context->GetRequestContextForRenderProcess(GetID())); + scoped_refptr<net::URLRequestContextGetter> media_request_context( + browser_context->GetMediaRequestContextForRenderProcess(GetID())); + + ResourceMessageFilter::GetContextsCallback get_contexts_callback( + base::Bind(&GetContexts, browser_context->GetResourceContext(), + request_context, media_request_context)); + ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( - GetID(), PROCESS_TYPE_RENDERER, resource_context, + GetID(), PROCESS_TYPE_RENDERER, storage_partition_impl_->GetAppCacheService(), ChromeBlobStorageContext::GetFor(browser_context), storage_partition_impl_->GetFileSystemContext(), - new RendererURLRequestContextSelector(browser_context, GetID())); + get_contexts_callback); channel_->AddFilter(resource_message_filter); MediaStreamManager* media_stream_manager = @@ -617,13 +583,16 @@ void RenderProcessHostImpl::CreateMessageFilters() { channel_->AddFilter(new AudioInputRendererHost( audio_manager, media_stream_manager, - BrowserMainLoop::GetInstance()->audio_mirroring_manager())); + BrowserMainLoop::GetInstance()->audio_mirroring_manager(), + BrowserMainLoop::GetInstance()->user_input_monitor())); channel_->AddFilter(new AudioRendererHost( - GetID(), audio_manager, + GetID(), + audio_manager, BrowserMainLoop::GetInstance()->audio_mirroring_manager(), - media_internals, media_stream_manager)); + media_internals, + media_stream_manager)); channel_->AddFilter( - new MIDIHost(BrowserMainLoop::GetInstance()->midi_manager())); + new MIDIHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager())); channel_->AddFilter(new MIDIDispatcherHost(GetID(), browser_context)); channel_->AddFilter(new VideoCaptureHost(media_stream_manager)); channel_->AddFilter(new AppCacheDispatcherHost( @@ -636,6 +605,8 @@ void RenderProcessHostImpl::CreateMessageFilters() { channel_->AddFilter(new IndexedDBDispatcherHost( GetID(), storage_partition_impl_->GetIndexedDBContext())); + channel_->AddFilter(new ServiceWorkerDispatcherHost( + storage_partition_impl_->GetServiceWorkerContext())); if (IsGuest()) { if (!g_browser_plugin_geolocation_context.Get().get()) { g_browser_plugin_geolocation_context.Get() = @@ -660,8 +631,6 @@ void RenderProcessHostImpl::CreateMessageFilters() { new DeviceRequestMessageFilter(resource_context, media_stream_manager)); #endif #if defined(ENABLE_PLUGINS) - // TODO(raymes): PepperMessageFilter should be removed from here. - channel_->AddFilter(new PepperMessageFilter(GetID(), browser_context)); channel_->AddFilter(new PepperRendererConnection(GetID())); #endif #if defined(ENABLE_INPUT_SPEECH) @@ -685,12 +654,19 @@ void RenderProcessHostImpl::CreateMessageFilters() { channel_->AddFilter(new TextInputClientMessageFilter(GetID())); #elif defined(OS_WIN) channel_->AddFilter(new FontCacheDispatcher()); +#elif defined(OS_ANDROID) + browser_demuxer_android_ = new BrowserDemuxerAndroid(); + channel_->AddFilter(browser_demuxer_android_); #endif + SocketStreamDispatcherHost::GetRequestContextCallback + request_context_callback( + base::Bind(&GetRequestContext, request_context, + media_request_context)); + SocketStreamDispatcherHost* socket_stream_dispatcher_host = - new SocketStreamDispatcherHost(GetID(), - new RendererURLRequestContextSelector(browser_context, GetID()), - resource_context); + new SocketStreamDispatcherHost( + GetID(), request_context_callback, resource_context); channel_->AddFilter(socket_stream_dispatcher_host); channel_->AddFilter(new WorkerMessageFilter( @@ -856,6 +832,15 @@ void RenderProcessHostImpl::AppendRendererCommandLine( field_trial_states); } + if (content::IsThreadedCompositingEnabled()) + command_line->AppendSwitch(switches::kEnableThreadedCompositing); + + if (content::IsDelegatedRendererEnabled()) + command_line->AppendSwitch(switches::kEnableDelegatedRenderer); + + if (content::IsDeadlineSchedulingEnabled()) + command_line->AppendSwitch(switches::kEnableDeadlineScheduling); + GetContentClient()->browser()->AppendExtraCommandLineSwitches( command_line, GetID()); @@ -875,6 +860,8 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kAudioBufferSize, switches::kAuditAllHandles, switches::kAuditHandles, + switches::kBlockCrossSiteDocuments, + switches::kDirectNPAPIRequests, switches::kDisable3DAPIs, switches::kDisableAcceleratedCompositing, switches::kDisableAcceleratedVideoDecode, @@ -882,6 +869,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableAudio, switches::kDisableBreakpad, switches::kDisableDatabases, + switches::kDisableDeadlineScheduling, switches::kDisableDelegatedRenderer, switches::kDisableDesktopNotifications, switches::kDisableDeviceOrientation, @@ -894,7 +882,6 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableHistogramCustomizer, switches::kDisableLocalStorage, switches::kDisableLogging, - switches::kDisableNewDialogStyle, switches::kDisableSeccompFilterSandbox, switches::kDisableSessionStorage, switches::kDisableSharedWorkers, @@ -908,7 +895,9 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableWebAudio, #if defined(ENABLE_WEBRTC) switches::kDisableDeviceEnumeration, - switches::kEnableSCTPDataChannels, + switches::kDisableSCTPDataChannels, + switches::kDisableWebRtcHWDecoding, + switches::kDisableWebRtcHWEncoding, #endif switches::kEnableWebAnimationsCSS, switches::kEnableWebAnimationsSVG, @@ -921,10 +910,15 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableBrowserInputController, switches::kEnableBrowserPluginForAllViewTypes, switches::kEnableDCHECK, + switches::kEnableDeadlineScheduling, switches::kEnableDelegatedRenderer, switches::kEnableEncryptedMedia, switches::kDisableLegacyEncryptedMedia, switches::kOverrideEncryptedMediaCanPlayType, +#if defined(OS_ANDROID) + switches::kEnableMediaDrm, + switches::kMediaDrmEnableNonCompositing, +#endif switches::kEnableExperimentalWebPlatformFeatures, switches::kEnableFixedLayout, switches::kEnableDeferredImageDecoding, @@ -932,7 +926,12 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableGPUClientLogging, switches::kEnableGpuClientTracing, switches::kEnableGpuBenchmarking, +#if defined(OS_WIN) + switches::kEnableHighResolutionTime, +#endif + switches::kEnableMP3StreamParser, switches::kEnableMemoryBenchmarking, + switches::kEnableOverlayFullscreenVideo, switches::kEnableOverlayScrollbars, switches::kEnableSkiaBenchmarking, switches::kEnableLogging, @@ -942,13 +941,11 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( #if defined(ENABLE_WEBRTC) switches::kEnableWebRtcAecRecordings, switches::kEnableWebRtcTcpServerSocket, - switches::kEnableWebRtcHWDecoding, #endif switches::kDisableWebKitMediaSource, switches::kEnableOverscrollNotifications, switches::kEnableStrictSiteIsolation, switches::kDisableFullScreen, - switches::kEnableNewDialogStyle, #if defined(ENABLE_PLUGINS) switches::kEnablePepperTesting, switches::kDisablePepper3d, @@ -981,7 +978,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableViewport, switches::kEnableInbandTextTracks, switches::kEnableOpusPlayback, - switches::kEnableVp8AlphaPlayback, + switches::kDisableVp8AlphaPlayback, switches::kEnableEac3Playback, switches::kForceDeviceScaleFactor, switches::kFullMemoryCrashReport, @@ -1030,19 +1027,21 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kWebCoreLogChannels, switches::kEnableWebGLDraftExtensions, switches::kEnableHTMLImports, + switches::kEnableInputModeAttribute, switches::kTraceToConsole, - switches::kEnableDeviceMotion, -#if defined(OS_ANDROID) switches::kDisableDeviceMotion, -#endif // Please keep these in alphabetical order. Compositor switches here should // also be added to chrome/browser/chromeos/login/chrome_restart_request.cc. cc::switches::kBackgroundColorInsteadOfCheckerboard, cc::switches::kCompositeToMailbox, cc::switches::kDisableCompositedAntialiasing, cc::switches::kDisableImplSidePainting, + cc::switches::kDisableLCDText, + cc::switches::kDisableMapImage, cc::switches::kDisableThreadedAnimation, cc::switches::kEnableImplSidePainting, + cc::switches::kEnableLCDText, + cc::switches::kEnableMapImage, cc::switches::kEnablePartialSwap, cc::switches::kEnablePerTilePainting, cc::switches::kEnablePinchVirtualViewport, @@ -1066,7 +1065,6 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( cc::switches::kTopControlsHideThreshold, cc::switches::kTopControlsShowThreshold, cc::switches::kTraceOverdraw, - cc::switches::kUseMapImage, }; renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, arraysize(kSwitchNames)); @@ -1249,7 +1247,6 @@ bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) { // Adding single handlers for your service here is fine, but once your // service needs more than one handler, please extract them into a new // message filter and add that filter to CreateMessageFilters(). - IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP_EX() if (!msg_is_ok) { @@ -1613,11 +1610,6 @@ void RenderProcessHostImpl::RegisterProcessHostForSite( map->RegisterProcess(site, process); } -base::MessageLoop* - RenderProcessHostImpl::GetInProcessRendererThreadForTesting() { - return g_in_process_thread; -} - void RenderProcessHostImpl::ProcessDied(bool already_dead) { // Our child process has died. If we didn't expect it, it's a crash. // In any case, we need to let everyone know it's gone. @@ -1661,10 +1653,11 @@ void RenderProcessHostImpl::ProcessDied(bool already_dead) { int RenderProcessHostImpl::GetActiveViewCount() { int num_active_views = 0; - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { // Count only RenderWidgetHosts in this process. - if (widgets[i]->GetProcess()->GetID() == GetID()) + if (widget->GetProcess()->GetID() == GetID()) num_active_views++; } return num_active_views; @@ -1797,17 +1790,17 @@ void RenderProcessHostImpl::OnCompositorSurfaceBuffersSwappedNoHost( void RenderProcessHostImpl::OnGpuSwitching() { // We are updating all widgets including swapped out ones. - RenderWidgetHost::List widgets = - RenderWidgetHostImpl::GetAllRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { - if (!widgets[i]->IsRenderView()) + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHostImpl::GetAllRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { + if (!widget->IsRenderView()) continue; // Skip widgets in other processes. - if (widgets[i]->GetProcess()->GetID() != GetID()) + if (widget->GetProcess()->GetID() != GetID()) continue; - RenderViewHost* rvh = RenderViewHost::From(widgets[i]); + RenderViewHost* rvh = RenderViewHost::From(widget); rvh->UpdateWebkitPreferences(rvh->GetWebkitPreferences()); } } diff --git a/chromium/content/browser/renderer_host/render_process_host_impl.h b/chromium/content/browser/renderer_host/render_process_host_impl.h index 72ec846303d..0e9ae1b2ad1 100644 --- a/chromium/content/browser/renderer_host/render_process_host_impl.h +++ b/chromium/content/browser/renderer_host/render_process_host_impl.h @@ -33,6 +33,7 @@ class Size; } namespace content { +class BrowserDemuxerAndroid; class GpuMessageFilter; class PeerConnectionTrackerHost; class RendererMainThread; @@ -177,6 +178,12 @@ class CONTENT_EXPORT RenderProcessHostImpl static base::MessageLoop* GetInProcessRendererThreadForTesting(); +#if defined(OS_ANDROID) + const scoped_refptr<BrowserDemuxerAndroid>& browser_demuxer_android() { + return browser_demuxer_android_; + } +#endif + protected: // A proxy for our IPC::Channel that lives on the IO thread (see // browser_process.h) @@ -264,10 +271,8 @@ class CONTENT_EXPORT RenderProcessHostImpl // This is used to clear our cache five seconds after the last use. base::DelayTimer<RenderProcessHostImpl> cached_dibs_cleaner_; -#if !defined(CHROME_MULTIPLE_DLL) // Used in single-process mode. - scoped_ptr<RendererMainThread> in_process_renderer_; -#endif + scoped_ptr<base::Thread> in_process_renderer_; // True after Init() has been called. We can't just check channel_ because we // also reset that in the case of process termination. @@ -324,6 +329,10 @@ class CONTENT_EXPORT RenderProcessHostImpl // Forwards power state messages to the renderer process. PowerMonitorMessageBroadcaster power_monitor_broadcaster_; +#if defined(OS_ANDROID) + scoped_refptr<BrowserDemuxerAndroid> browser_demuxer_android_; +#endif + DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImpl); }; diff --git a/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc b/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc index 87d31dca88c..81537181532 100644 --- a/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc +++ b/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc @@ -30,6 +30,8 @@ #include "content/child/webkitplatformsupport_impl.h" #include "content/common/font_config_ipc_linux.h" #include "content/common/sandbox_linux.h" +#include "content/common/set_process_title.h" +#include "content/public/common/content_switches.h" #include "skia/ext/skia_utils_base.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/linux/WebFontInfo.h" @@ -71,6 +73,15 @@ class SandboxIPCProcess { // positioning, so we pass the current setting through to WebKit. WebFontInfo::setSubpixelPositioning( gfx::GetDefaultWebkitSubpixelPositioning()); + + CommandLine& command_line = *CommandLine::ForCurrentProcess(); + command_line.AppendSwitchASCII(switches::kProcessType, + switches::kSandboxIPCProcess); + + // Update the process title. The argv was already cached by the call to + // SetProcessTitleFromCommandLine in content_main_runner.cc, so we can pass + // NULL here (we don't have the original argv at this point). + SetProcessTitleFromCommandLine(NULL); } ~SandboxIPCProcess(); diff --git a/chromium/content/browser/renderer_host/render_view_host_browsertest.cc b/chromium/content/browser/renderer_host/render_view_host_browsertest.cc index 4255d055ce1..20fec727501 100644 --- a/chromium/content/browser/renderer_host/render_view_host_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_view_host_browsertest.cc @@ -13,7 +13,7 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_paths.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/host_port_pair.h" diff --git a/chromium/content/browser/renderer_host/render_view_host_delegate.h b/chromium/content/browser/renderer_host/render_view_host_delegate.h index 5ce2a44d716..997452aeb54 100644 --- a/chromium/content/browser/renderer_host/render_view_host_delegate.h +++ b/chromium/content/browser/renderer_host/render_view_host_delegate.h @@ -50,6 +50,7 @@ class PageState; class RenderViewHost; class RenderViewHostDelegateView; class SessionStorageNamespace; +class SiteInstance; class WebContents; class WebContentsImpl; struct ContextMenuParams; @@ -58,7 +59,8 @@ struct GlobalRequestID; struct NativeWebKeyboardEvent; struct Referrer; struct RendererPreferences; -class SiteInstance; +struct ResourceRedirectDetails; +struct ResourceRequestDetails; // // RenderViewHostDelegate @@ -151,8 +153,8 @@ class CONTENT_EXPORT RenderViewHostDelegate { // The RenderView processed a redirect during a provisional load. // // TODO(creis): Remove this method and have the pre-rendering code listen to - // the ResourceDispatcherHost's RESOURCE_RECEIVED_REDIRECT notification - // instead. See http://crbug.com/78512. + // WebContentsObserver::DidGetRedirectForResourceRequest instead. + // See http://crbug.com/78512. virtual void DidRedirectProvisionalLoad( RenderViewHost* render_view_host, int32 page_id, @@ -164,6 +166,14 @@ class CONTENT_EXPORT RenderViewHostDelegate { RenderViewHost* render_view_host, const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params) {} + // A response has been received for a resource request. + virtual void DidGetResourceResponseStart( + const ResourceRequestDetails& details) {} + + // A redirect was received while requesting a resource. + virtual void DidGetRedirectForResourceRequest( + const ResourceRedirectDetails& details) {} + // The RenderView was navigated to a different page. virtual void DidNavigate(RenderViewHost* render_view_host, const ViewHostMsg_FrameNavigate_Params& params) {} @@ -299,6 +309,7 @@ class CONTENT_EXPORT RenderViewHostDelegate { // Notification that the renderer has become unresponsive. The // delegate can use this notification to show a warning to the user. virtual void RendererUnresponsive(RenderViewHost* render_view_host, + bool is_during_before_unload, bool is_during_unload) {} // Notification that a previously unresponsive renderer has become diff --git a/chromium/content/browser/renderer_host/render_view_host_factory.cc b/chromium/content/browser/renderer_host/render_view_host_factory.cc index 20d15d38f0c..5cffd134b8e 100644 --- a/chromium/content/browser/renderer_host/render_view_host_factory.cc +++ b/chromium/content/browser/renderer_host/render_view_host_factory.cc @@ -19,14 +19,15 @@ RenderViewHost* RenderViewHostFactory::Create( RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out) { + bool swapped_out, + bool hidden) { if (factory_) { return factory_->CreateRenderViewHost(instance, delegate, widget_delegate, routing_id, main_frame_routing_id, swapped_out); } return new RenderViewHostImpl(instance, delegate, widget_delegate, routing_id, - main_frame_routing_id, swapped_out); + main_frame_routing_id, swapped_out, hidden); } // static diff --git a/chromium/content/browser/renderer_host/render_view_host_factory.h b/chromium/content/browser/renderer_host/render_view_host_factory.h index 6226187af53..b0466355d49 100644 --- a/chromium/content/browser/renderer_host/render_view_host_factory.h +++ b/chromium/content/browser/renderer_host/render_view_host_factory.h @@ -29,7 +29,8 @@ class RenderViewHostFactory { RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out); + bool swapped_out, + bool hidden); // Returns true if there is currently a globally-registered factory. static bool has_factory() { diff --git a/chromium/content/browser/renderer_host/render_view_host_impl.cc b/chromium/content/browser/renderer_host/render_view_host_impl.cc index 56801c03c18..68204fb746a 100644 --- a/chromium/content/browser/renderer_host/render_view_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_view_host_impl.cc @@ -51,6 +51,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_view_host_observer.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/content_constants.h" @@ -72,7 +73,7 @@ #if defined(OS_MACOSX) #include "content/browser/renderer_host/popup_menu_helper_mac.h" #elif defined(OS_ANDROID) -#include "media/base/android/media_player_manager.h" +#include "content/browser/media/android/browser_media_player_manager.h" #endif using base::TimeDelta; @@ -116,11 +117,7 @@ g_created_callbacks = LAZY_INSTANCE_INITIALIZER; // static RenderViewHost* RenderViewHost::FromID(int render_process_id, int render_view_id) { - RenderWidgetHost* widget = - RenderWidgetHost::FromID(render_process_id, render_view_id); - if (!widget || !widget->IsRenderView()) - return NULL; - return static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(widget)); + return RenderViewHostImpl::FromID(render_process_id, render_view_id); } // static @@ -143,8 +140,11 @@ void RenderViewHost::FilterURL(const RenderProcessHost* process, // static RenderViewHostImpl* RenderViewHostImpl::FromID(int render_process_id, int render_view_id) { - return static_cast<RenderViewHostImpl*>( - RenderViewHost::FromID(render_process_id, render_view_id)); + RenderWidgetHost* widget = + RenderWidgetHost::FromID(render_process_id, render_view_id); + if (!widget || !widget->IsRenderView()) + return NULL; + return static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(widget)); } RenderViewHostImpl::RenderViewHostImpl( @@ -153,8 +153,12 @@ RenderViewHostImpl::RenderViewHostImpl( RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out) - : RenderWidgetHostImpl(widget_delegate, instance->GetProcess(), routing_id), + bool swapped_out, + bool hidden) + : RenderWidgetHostImpl(widget_delegate, + instance->GetProcess(), + routing_id, + hidden), delegate_(delegate), instance_(static_cast<SiteInstanceImpl*>(instance)), waiting_for_drag_context_response_(false), @@ -191,7 +195,7 @@ RenderViewHostImpl::RenderViewHostImpl( instance_->increment_active_view_count(); #if defined(OS_ANDROID) - media_player_manager_ = media::MediaPlayerManager::Create(this); + media_player_manager_ = BrowserMediaPlayerManager::Create(this); #endif } @@ -259,6 +263,7 @@ bool RenderViewHostImpl::CreateRenderView( // Ensure the RenderView sets its opener correctly. params.opener_route_id = opener_route_id; params.swapped_out = is_swapped_out_; + params.hidden = is_hidden(); params.next_page_id = next_page_id; GetWebScreenInfo(¶ms.screen_info); params.accessibility_mode = accessibility_mode(); @@ -338,7 +343,7 @@ void RenderViewHostImpl::Navigate(const ViewMsg_Navigate_Params& params) { // // WebKit doesn't send throb notifications for JavaScript URLs, so we // don't want to either. - if (!params.url.SchemeIs(chrome::kJavaScriptScheme)) + if (!params.url.SchemeIs(kJavaScriptScheme)) delegate_->DidStartLoading(this); FOR_EACH_OBSERVER(RenderViewHostObserver, observers_, Navigate(params.url)); @@ -479,9 +484,10 @@ void RenderViewHostImpl::WasSwappedOut() { // Count the number of active widget hosts for the process, which // is equivalent to views using the process as of this writing. - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { - if (widgets[i]->GetProcess()->GetID() == GetProcess()->GetID()) + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { + if (widget->GetProcess()->GetID() == GetProcess()->GetID()) ++views; } @@ -736,7 +742,8 @@ void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg, // This must be done after sending the reply since RenderView can't close // correctly while waiting for a response. if (is_waiting && are_javascript_messages_suppressed_) - delegate_->RendererUnresponsive(this, is_waiting); + delegate_->RendererUnresponsive( + this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_); } void RenderViewHostImpl::DragSourceEndedAt( @@ -763,6 +770,12 @@ void RenderViewHostImpl::DragSourceSystemDragEnded() { } void RenderViewHostImpl::AllowBindings(int bindings_flags) { + // Never grant any bindings to browser plugin guests. + if (GetProcess()->IsGuest()) { + NOTREACHED() << "Never grant bindings to a guest process."; + return; + } + // Ensure we aren't granting WebUI bindings to a process that has already // been used for non-privileged views. if (bindings_flags & BINDINGS_POLICY_WEB_UI && @@ -777,12 +790,6 @@ void RenderViewHostImpl::AllowBindings(int bindings_flags) { return; } - // Never grant any bindings to browser plugin guests. - if (GetProcess()->IsGuest()) { - NOTREACHED() << "Never grant bindings to a guest process."; - return; - } - if (bindings_flags & BINDINGS_POLICY_WEB_UI) { ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings( GetProcess()->GetID()); @@ -999,8 +1006,7 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) { OnDidAccessInitialDocument) IPC_MESSAGE_HANDLER(ViewHostMsg_DomOperationResponse, OnDomOperationResponse) - IPC_MESSAGE_HANDLER(AccessibilityHostMsg_Notifications, - OnAccessibilityNotifications) + IPC_MESSAGE_HANDLER(AccessibilityHostMsg_Events, OnAccessibilityEvents) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED( handled = RenderWidgetHostImpl::OnMessageReceived(msg)) @@ -1016,6 +1022,11 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) { return handled; } +void RenderViewHostImpl::Init() { + RenderWidgetHostImpl::Init(); + main_render_frame_host()->Init(); +} + void RenderViewHostImpl::Shutdown() { // If we are being run modally (see RunModal), then we need to cleanup. if (run_modal_reply_msg_) { @@ -1239,8 +1250,6 @@ void RenderViewHostImpl::OnNavigate(const IPC::Message& msg) { FilterURL(policy, process, false, &(*it)); } FilterURL(policy, process, true, &validated_params.searchable_form_url); - FilterURL(policy, process, true, &validated_params.password_form.origin); - FilterURL(policy, process, true, &validated_params.password_form.action); // Without this check, the renderer can trick the browser into using // filenames it can't access in a future session restore. @@ -1354,6 +1363,8 @@ void RenderViewHostImpl::OnContextMenu(const ContextMenuParams& params) { void RenderViewHostImpl::OnToggleFullscreen(bool enter_fullscreen) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); delegate_->ToggleFullscreenMode(enter_fullscreen); + // We need to notify the contents that its fullscreen state has changed. This + // is done as part of the resize message. WasResized(); } @@ -1399,7 +1410,7 @@ void RenderViewHostImpl::OnDidChangeNumWheelEvents(int count) { void RenderViewHostImpl::OnSelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { if (view_) view_->SelectionChanged(text, offset, range); } @@ -1464,7 +1475,7 @@ void RenderViewHostImpl::OnStartDragging( ChildProcessSecurityPolicyImpl::GetInstance(); // Allow drag of Javascript URLs to enable bookmarklet drag to bookmark bar. - if (!filtered_data.url.SchemeIs(chrome::kJavaScriptScheme)) + if (!filtered_data.url.SchemeIs(kJavaScriptScheme)) FilterURL(policy, process, true, &filtered_data.url); FilterURL(policy, process, false, &filtered_data.html_base_url); // Filter out any paths that the renderer didn't have access to. This prevents @@ -1522,6 +1533,7 @@ void RenderViewHostImpl::OnAddMessageToConsole( const string16& source_id) { if (delegate_->AddMessageToConsole(level, message, line_no, source_id)) return; + // Pass through log level only on WebUI pages to limit console spew. int32 resolved_level = HasWebUIScheme(delegate_->GetURL()) ? level : 0; @@ -1595,7 +1607,7 @@ void RenderViewHostImpl::OnClosePageACK() { void RenderViewHostImpl::NotifyRendererUnresponsive() { delegate_->RendererUnresponsive( - this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_); + this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_); } void RenderViewHostImpl::NotifyRendererResponsive() { @@ -1772,9 +1784,8 @@ void RenderViewHostImpl::SetAltErrorPageURL(const GURL& url) { void RenderViewHostImpl::ExitFullscreen() { RejectMouseLockOrUnlockIfNecessary(); - // We need to notify the contents that its fullscreen state has changed. This - // is done as part of the resize message. - WasResized(); + // Notify delegate_ and renderer of fullscreen state change. + OnToggleFullscreen(false); } WebPreferences RenderViewHostImpl::GetWebkitPreferences() { @@ -1789,7 +1800,7 @@ void RenderViewHostImpl::DisownOpener() { } void RenderViewHostImpl::SetAccessibilityCallbackForTesting( - const base::Callback<void(AccessibilityNotification)>& callback) { + const base::Callback<void(WebKit::WebAXEvent)>& callback) { accessibility_testing_callback_ = callback; } @@ -1871,10 +1882,6 @@ void RenderViewHostImpl::ExecutePluginActionAtLocation( Send(new ViewMsg_PluginActionAt(GetRoutingID(), location, action)); } -void RenderViewHostImpl::DisassociateFromPopupCount() { - Send(new ViewMsg_DisassociateFromPopupCount(GetRoutingID())); -} - void RenderViewHostImpl::NotifyMoveOrResizeStarted() { Send(new ViewMsg_MoveOrResizeStarted(GetRoutingID())); } @@ -1883,13 +1890,13 @@ void RenderViewHostImpl::StopFinding(StopFindAction action) { Send(new ViewMsg_StopFinding(GetRoutingID(), action)); } -void RenderViewHostImpl::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderViewHostImpl::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (view_ && !is_swapped_out_) - view_->OnAccessibilityNotifications(params); + view_->OnAccessibilityEvents(params); // Always send an ACK or the renderer can be in a bad state. - Send(new AccessibilityMsg_Notifications_ACK(GetRoutingID())); + Send(new AccessibilityMsg_Events_ACK(GetRoutingID())); // The rest of this code is just for testing; bail out if we're not // in that mode. @@ -1897,10 +1904,10 @@ void RenderViewHostImpl::OnAccessibilityNotifications( return; for (unsigned i = 0; i < params.size(); i++) { - const AccessibilityHostMsg_NotificationParams& param = params[i]; - AccessibilityNotification src_type = param.notification_type; - if (src_type == AccessibilityNotificationLayoutComplete || - src_type == AccessibilityNotificationLoadComplete) { + const AccessibilityHostMsg_EventParams& param = params[i]; + WebKit::WebAXEvent src_type = param.event_type; + if (src_type == WebKit::WebAXEventLayoutComplete || + src_type == WebKit::WebAXEventLoadComplete) { MakeAccessibilityNodeDataTree(param.nodes, &accessibility_tree_); } accessibility_testing_callback_.Run(src_type); @@ -1953,7 +1960,7 @@ void RenderViewHostImpl::OnShowDesktopNotification( // allows unwanted cross-domain access. GURL url = params.contents_url; if (params.is_html && - (url.SchemeIs(chrome::kJavaScriptScheme) || + (url.SchemeIs(kJavaScriptScheme) || url.SchemeIs(chrome::kFileScheme))) { return; } @@ -2024,6 +2031,11 @@ void RenderViewHostImpl::OnShowPopup( } #endif +RenderFrameHostImpl* RenderViewHostImpl::main_render_frame_host() const { + DCHECK_EQ(GetProcess(), main_render_frame_host_->GetProcess()); + return main_render_frame_host_.get(); +} + void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) { // We update the number of RenderViews in a SiteInstance when the // swapped out status of this RenderView gets flipped. diff --git a/chromium/content/browser/renderer_host/render_view_host_impl.h b/chromium/content/browser/renderer_host/render_view_host_impl.h index 20caaf5ea73..c4bfb7876d1 100644 --- a/chromium/content/browser/renderer_host/render_view_host_impl.h +++ b/chromium/content/browser/renderer_host/render_view_host_impl.h @@ -18,22 +18,22 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/site_instance_impl.h" #include "content/common/accessibility_node_data.h" -#include "content/common/accessibility_notification.h" #include "content/common/drag_event_source_info.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/render_view_host.h" #include "content/public/common/javascript_message_type.h" #include "content/public/common/window_container_type.h" #include "net/base/load_states.h" -#include "third_party/skia/include/core/SkColor.h" +#include "third_party/WebKit/public/web/WebAXEnums.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" #include "third_party/WebKit/public/web/WebPopupType.h" #include "third_party/WebKit/public/web/WebTextDirection.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/base/window_open_disposition.h" class SkBitmap; class ViewMsg_Navigate; -struct AccessibilityHostMsg_NotificationParams; +struct AccessibilityHostMsg_EventParams; struct MediaPlayerAction; struct ViewHostMsg_CreateWindow_Params; struct ViewHostMsg_DidFailProvisionalLoadWithError_Params; @@ -48,19 +48,17 @@ namespace base { class ListValue; } -namespace ui { +namespace gfx { class Range; -struct SelectedFileInfo; } -#if defined(OS_ANDROID) -namespace media { -class MediaPlayerManager; +namespace ui { +struct SelectedFileInfo; } -#endif namespace content { +class BrowserMediaPlayerManager; class ChildProcessSecurityPolicyImpl; class PageState; class RenderFrameHostImpl; @@ -110,7 +108,8 @@ class CONTENT_EXPORT RenderViewHostImpl // |routing_id| could be a valid route id, or it could be MSG_ROUTING_NONE, in // which case RenderWidgetHost will create a new one. |swapped_out| indicates // whether the view should initially be swapped out (e.g., for an opener - // frame being rendered by another process). + // frame being rendered by another process). |hidden| indicates whether the + // view is initially hidden or visible. // // The |session_storage_namespace| parameter allows multiple render views and // WebContentses to share the same session storage (part of the WebStorage @@ -123,7 +122,8 @@ class CONTENT_EXPORT RenderViewHostImpl RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, - bool swapped_out); + bool swapped_out, + bool hidden); virtual ~RenderViewHostImpl(); // RenderViewHost implementation. @@ -131,7 +131,6 @@ class CONTENT_EXPORT RenderViewHostImpl virtual void ClearFocusedNode() OVERRIDE; virtual void ClosePage() OVERRIDE; virtual void CopyImageAt(int x, int y) OVERRIDE; - virtual void DisassociateFromPopupCount() OVERRIDE; virtual void DesktopNotificationPermissionRequestDone( int callback_context) OVERRIDE; virtual void DesktopNotificationPostDisplay(int callback_context) OVERRIDE; @@ -354,6 +353,7 @@ class CONTENT_EXPORT RenderViewHostImpl } // RenderWidgetHost public overrides. + virtual void Init() OVERRIDE; virtual void Shutdown() OVERRIDE; virtual bool IsRenderView() const OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; @@ -388,7 +388,7 @@ class CONTENT_EXPORT RenderViewHostImpl #endif #if defined(OS_ANDROID) - media::MediaPlayerManager* media_player_manager() { + BrowserMediaPlayerManager* media_player_manager() { return media_player_manager_; } @@ -417,7 +417,7 @@ class CONTENT_EXPORT RenderViewHostImpl // renderer process, and the accessibility tree it sent can be // retrieved using accessibility_tree_for_testing(). void SetAccessibilityCallbackForTesting( - const base::Callback<void(AccessibilityNotification)>& callback); + const base::Callback<void(WebKit::WebAXEvent)>& callback); // Only valid if SetAccessibilityCallbackForTesting was called and // the callback was run at least once. Returns a snapshot of the @@ -526,7 +526,7 @@ class CONTENT_EXPORT RenderViewHostImpl void OnDidChangeNumWheelEvents(int count); void OnSelectionChanged(const string16& text, size_t offset, - const ui::Range& range); + const gfx::Range& range); void OnSelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params); void OnPasteFromSelectionClipboard(); @@ -562,8 +562,8 @@ class CONTENT_EXPORT RenderViewHostImpl const base::TimeTicks& renderer_before_unload_end_time); void OnClosePageACK(); void OnSwapOutACK(); - void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params); + void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params); void OnScriptEvalResponse(int id, const base::ListValue& result); void OnDidZoomURL(double zoom_level, bool remember, const GURL& url); void OnRequestDesktopNotificationPermission(const GURL& origin, @@ -584,6 +584,11 @@ class CONTENT_EXPORT RenderViewHostImpl private: friend class TestRenderViewHost; FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, BasicRenderFrameHost); + FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, RoutingIdSane); + + // TODO(nasko): Remove this accessor once RenderFrameHost moves into the frame + // tree. + RenderFrameHostImpl* main_render_frame_host() const; // Sets whether this RenderViewHost is swapped out in favor of another, // and clears any waiting state that is no longer relevant. @@ -678,8 +683,7 @@ class CONTENT_EXPORT RenderViewHostImpl std::map<int, JavascriptResultCallback> javascript_callbacks_; // Accessibility callback for testing. - base::Callback<void(AccessibilityNotification)> - accessibility_testing_callback_; + base::Callback<void(WebKit::WebAXEvent)> accessibility_testing_callback_; // The most recently received accessibility tree - for testing only. AccessibilityNodeDataTreeNode accessibility_tree_; @@ -699,7 +703,7 @@ class CONTENT_EXPORT RenderViewHostImpl #if defined(OS_ANDROID) // Manages all the android mediaplayer objects and handling IPCs for video. // This class inherits from RenderViewHostObserver. - media::MediaPlayerManager* media_player_manager_; + BrowserMediaPlayerManager* media_player_manager_; #endif DISALLOW_COPY_AND_ASSIGN(RenderViewHostImpl); diff --git a/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc b/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc index 4053d6593f2..55785d07859 100644 --- a/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_view_host_manager_browsertest.cc @@ -25,7 +25,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/net_util.h" @@ -86,7 +86,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -153,7 +153,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the window to open. Shell* new_shell = new_shell_observer.GetShell(); - EXPECT_EQ("/files/title2.html", new_shell->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + new_shell->web_contents()->GetVisibleURL().path()); // Wait for the cross-site transition in the new tab to finish. WaitForLoadStop(new_shell->web_contents()); @@ -207,7 +208,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, Shell* new_shell = new_shell_observer.GetShell(); // Opens in new window. - EXPECT_EQ("/files/title2.html", new_shell->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + new_shell->web_contents()->GetVisibleURL().path()); // Wait for the cross-site transition in the new tab to finish. WaitForLoadStop(new_shell->web_contents()); @@ -262,7 +264,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the cross-site transition in the new tab to finish. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/title2.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -308,7 +310,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Opens in same window. EXPECT_EQ(1u, Shell::windows().size()); - EXPECT_EQ("/files/title2.html", shell()->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + shell()->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> noref_site_instance( @@ -376,7 +379,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new tab to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -421,7 +424,13 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Test that setting the opener to null in a window affects cross-process // navigations, including those to existing entries. http://crbug.com/156669. -IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, DisownOpener) { +// Flaky on windows: http://crbug.com/291249 +#if defined(OS_WIN) +#define MAYBE_DisownOpener DISABLED_DisownOpener +#else +#define MAYBE_DisownOpener DisownOpener +#endif +IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, MAYBE_DisownOpener) { // Start two servers with different sites. ASSERT_TRUE(test_server()->Start()); net::SpawnedTestServer https_server( @@ -456,7 +465,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, DisownOpener) { // Wait for the navigation in the new tab to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/title2.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -562,7 +571,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // send it to post_message.html on a different site. WebContents* foo_contents = new_shell->web_contents(); WaitForLoadStop(foo_contents); - EXPECT_EQ("/files/navigate_opener.html", foo_contents->GetURL().path()); + EXPECT_EQ("/files/navigate_opener.html", + foo_contents->GetLastCommittedURL().path()); NavigateToURL(new_shell, https_server.GetURL("files/post_message.html")); scoped_refptr<SiteInstance> foo_site_instance( foo_contents->GetSiteInstance()); @@ -581,7 +591,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, Shell* new_shell2 = new_shell_observer2.GetShell(); WebContents* new_contents = new_shell2->web_contents(); WaitForLoadStop(new_contents); - EXPECT_EQ("/files/title2.html", new_contents->GetURL().path()); + EXPECT_EQ("/files/title2.html", new_contents->GetLastCommittedURL().path()); NavigateToURL(new_shell2, test_server()->GetURL("files/post_message.html")); EXPECT_EQ(orig_site_instance, new_contents->GetSiteInstance()); RenderViewHostManager* new_manager = @@ -698,7 +708,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> blank_site_instance( @@ -765,7 +775,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> opened_site_instance( @@ -829,7 +839,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ClickLinkAfter204Error) { scoped_refptr<SiteInstance> post_nav_site_instance( shell()->web_contents()->GetSiteInstance()); EXPECT_EQ(orig_site_instance, post_nav_site_instance); - EXPECT_EQ("/nocontent", shell()->web_contents()->GetURL().path()); + EXPECT_EQ("/nocontent", + shell()->web_contents()->GetVisibleURL().path()); EXPECT_EQ("/files/click-noreferrer-links.html", shell()->web_contents()->GetController(). GetLastCommittedEntry()->GetVirtualURL().path()); @@ -847,7 +858,8 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, ClickLinkAfter204Error) { // Opens in same tab. EXPECT_EQ(1u, Shell::windows().size()); - EXPECT_EQ("/files/title2.html", shell()->web_contents()->GetURL().path()); + EXPECT_EQ("/files/title2.html", + shell()->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. scoped_refptr<SiteInstance> noref_site_instance( @@ -1073,7 +1085,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new tab to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); RenderViewHost* rvh = new_shell->web_contents()->GetRenderViewHost(); @@ -1104,7 +1116,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, } EXPECT_EQ("/files/navigate_opener.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); EXPECT_EQ(rvh, new_shell->web_contents()->GetRenderViewHost()); @@ -1187,7 +1199,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, LeakingRenderViewHosts) { // view-source URL, we create a new SiteInstance. RenderViewHost* blank_rvh = shell()->web_contents()->GetRenderViewHost(); SiteInstance* blank_site_instance = blank_rvh->GetSiteInstance(); - EXPECT_EQ(shell()->web_contents()->GetURL(), GURL::EmptyGURL()); + EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL::EmptyGURL()); EXPECT_EQ(blank_site_instance->GetSiteURL(), GURL::EmptyGURL()); rvh_observers.AddObserverToRVH(blank_rvh); @@ -1269,7 +1281,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Wait for the navigation in the new window to finish, if it hasn't. WaitForLoadStop(new_shell->web_contents()); EXPECT_EQ("/files/title1.html", - new_shell->web_contents()->GetURL().path()); + new_shell->web_contents()->GetLastCommittedURL().path()); // Should have the same SiteInstance. EXPECT_EQ(orig_site_instance, new_shell->web_contents()->GetSiteInstance()); @@ -1286,7 +1298,7 @@ IN_PROC_BROWSER_TEST_F(RenderViewHostManagerTest, // Make sure it ends up at the right page. WaitForLoadStop(shell()->web_contents()); EXPECT_EQ(https_server.GetURL("files/title1.html"), - shell()->web_contents()->GetURL()); + shell()->web_contents()->GetLastCommittedURL()); EXPECT_EQ(new_site_instance, shell()->web_contents()->GetSiteInstance()); } diff --git a/chromium/content/browser/renderer_host/render_view_host_unittest.cc b/chromium/content/browser/renderer_host/render_view_host_unittest.cc index a38e0446f3d..f4d694fae42 100644 --- a/chromium/content/browser/renderer_host/render_view_host_unittest.cc +++ b/chromium/content/browser/renderer_host/render_view_host_unittest.cc @@ -288,4 +288,11 @@ TEST_F(RenderViewHostTest, NavigationWithBadHistoryItemFiles) { EXPECT_EQ(1, process()->bad_msg_count()); } +TEST_F(RenderViewHostTest, RoutingIdSane) { + EXPECT_EQ(test_rvh()->GetProcess(), + test_rvh()->main_render_frame_host()->GetProcess()); + EXPECT_NE(test_rvh()->GetRoutingID(), + test_rvh()->main_render_frame_host()->routing_id()); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc index 9f11299b1a6..bfdf6a5450a 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_browsertest.cc @@ -7,7 +7,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_paths.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/base/net_util.h" @@ -46,8 +46,14 @@ class RenderWidgetHostBrowserTest : public ContentBrowserTest { base::FilePath test_dir_; }; +// Disabled on Windows and CrOS because it is flaky: crbug.com/272379. +#if defined(OS_WIN) || defined(OS_CHROMEOS) +#define MAYBE_GetSnapshotFromRendererTest DISABLED_GetSnapshotFromRendererTest +#else +#define MAYBE_GetSnapshotFromRendererTest GetSnapshotFromRendererTest +#endif IN_PROC_BROWSER_TEST_F(RenderWidgetHostBrowserTest, - GetSnapshotFromRendererTest) { + MAYBE_GetSnapshotFromRendererTest) { base::RunLoop run_loop; NavigateToURL(shell(), GURL(net::FilePathToFileURL( diff --git a/chromium/content/browser/renderer_host/render_widget_host_impl.cc b/chromium/content/browser/renderer_host/render_widget_host_impl.cc index edd21fe5987..4fce8d8cb2d 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_impl.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_impl.cc @@ -22,12 +22,14 @@ #include "cc/output/compositor_frame.h" #include "cc/output/compositor_frame_ack.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/gpu/gpu_process_host_ui_shim.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/browser/renderer_host/backing_store.h" #include "content/browser/renderer_host/backing_store_manager.h" #include "content/browser/renderer_host/dip_util.h" +#include "content/browser/renderer_host/input/buffered_input_router.h" #include "content/browser/renderer_host/input/immediate_input_router.h" #include "content/browser/renderer_host/overscroll_controller.h" #include "content/browser/renderer_host/render_process_host_impl.h" @@ -40,10 +42,10 @@ #include "content/common/input_messages.h" #include "content/common/view_messages.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "content/public/browser/compositor_util.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" @@ -51,8 +53,8 @@ #include "skia/ext/image_operations.h" #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/web/WebCompositionUnderline.h" -#include "ui/base/events/event.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/event.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/size_conversions.h" #include "ui/gfx/skbitmap_operations.h" #include "ui/gfx/vector2d_conversions.h" @@ -91,15 +93,50 @@ const int kPaintMsgTimeoutMS = 50; base::LazyInstance<std::vector<RenderWidgetHost::CreatedCallback> > g_created_callbacks = LAZY_INSTANCE_INITIALIZER; -} // namespace - - typedef std::pair<int32, int32> RenderWidgetHostID; typedef base::hash_map<RenderWidgetHostID, RenderWidgetHostImpl*> RoutingIDWidgetMap; -static base::LazyInstance<RoutingIDWidgetMap> g_routing_id_widget_map = +base::LazyInstance<RoutingIDWidgetMap> g_routing_id_widget_map = LAZY_INSTANCE_INITIALIZER; +// Implements the RenderWidgetHostIterator interface. It keeps a list of +// RenderWidgetHosts, and makes sure it returns a live RenderWidgetHost at each +// iteration (or NULL if there isn't any left). +class RenderWidgetHostIteratorImpl : public RenderWidgetHostIterator { + public: + RenderWidgetHostIteratorImpl() + : current_index_(0) { + } + + virtual ~RenderWidgetHostIteratorImpl() { + } + + void Add(RenderWidgetHost* host) { + hosts_.push_back(RenderWidgetHostID(host->GetProcess()->GetID(), + host->GetRoutingID())); + } + + // RenderWidgetHostIterator: + virtual RenderWidgetHost* GetNextHost() OVERRIDE { + RenderWidgetHost* host = NULL; + while (current_index_ < hosts_.size() && !host) { + RenderWidgetHostID id = hosts_[current_index_]; + host = RenderWidgetHost::FromID(id.first, id.second); + ++current_index_; + } + return host; + } + + private: + std::vector<RenderWidgetHostID> hosts_; + size_t current_index_; + + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostIteratorImpl); +}; + +} // namespace + + // static void RenderWidgetHost::RemoveAllBackingStores() { BackingStoreManager::RemoveAllBackingStores(); @@ -115,7 +152,8 @@ size_t RenderWidgetHost::BackingStoreMemorySize() { RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, - int routing_id) + int routing_id, + bool hidden) : view_(NULL), renderer_initialized_(false), hung_renderer_delay_ms_(kHungRendererDelayMs), @@ -124,11 +162,12 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, routing_id_(routing_id), surface_id_(0), is_loading_(false), - is_hidden_(false), + is_hidden_(hidden), is_fullscreen_(false), is_accelerated_compositing_active_(false), repaint_ack_pending_(false), resize_ack_pending_(false), + screen_info_out_of_date_(false), overdraw_bottom_height_(0.f), should_auto_resize_(false), waiting_for_screen_rects_ack_(false), @@ -175,9 +214,11 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, g_routing_id_widget_map.Get().insert(std::make_pair( RenderWidgetHostID(process->GetID(), routing_id_), this)); process_->AddRoute(routing_id_, this); - // Because the widget initializes as is_hidden_ == false, - // tell the process host that we're alive. - process_->WidgetRestored(); + + // If we're initially visible, tell the process host that we're alive. + // Otherwise we'll notify the process host when we are first shown. + if (!hidden) + process_->WidgetRestored(); accessibility_mode_ = BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode(); @@ -185,7 +226,8 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, for (size_t i = 0; i < g_created_callbacks.Get().size(); i++) g_created_callbacks.Get().at(i).Run(this); - input_router_.reset(new ImmediateInputRouter(process, this, routing_id_)); + input_router_.reset( + new ImmediateInputRouter(process_, this, this, routing_id_)); #if defined(USE_AURA) bool overscroll_enabled = CommandLine::ForCurrentProcess()-> @@ -222,6 +264,7 @@ RenderWidgetHost* RenderWidgetHost::FromID( RenderWidgetHostImpl* RenderWidgetHostImpl::FromID( int32 process_id, int32 routing_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer(); RoutingIDWidgetMap::iterator it = widgets->find( RenderWidgetHostID(process_id, routing_id)); @@ -229,8 +272,8 @@ RenderWidgetHostImpl* RenderWidgetHostImpl::FromID( } // static -std::vector<RenderWidgetHost*> RenderWidgetHost::GetRenderWidgetHosts() { - std::vector<RenderWidgetHost*> hosts; +scoped_ptr<RenderWidgetHostIterator> RenderWidgetHost::GetRenderWidgetHosts() { + RenderWidgetHostIteratorImpl* hosts = new RenderWidgetHostIteratorImpl(); RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer(); for (RoutingIDWidgetMap::const_iterator it = widgets->begin(); it != widgets->end(); @@ -238,28 +281,31 @@ std::vector<RenderWidgetHost*> RenderWidgetHost::GetRenderWidgetHosts() { RenderWidgetHost* widget = it->second; if (!widget->IsRenderView()) { - hosts.push_back(widget); + hosts->Add(widget); continue; } // Add only active RenderViewHosts. RenderViewHost* rvh = RenderViewHost::From(widget); if (!static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out()) - hosts.push_back(widget); + hosts->Add(widget); } - return hosts; + + return scoped_ptr<RenderWidgetHostIterator>(hosts); } // static -std::vector<RenderWidgetHost*> RenderWidgetHostImpl::GetAllRenderWidgetHosts() { - std::vector<RenderWidgetHost*> hosts; +scoped_ptr<RenderWidgetHostIterator> +RenderWidgetHostImpl::GetAllRenderWidgetHosts() { + RenderWidgetHostIteratorImpl* hosts = new RenderWidgetHostIteratorImpl(); RoutingIDWidgetMap* widgets = g_routing_id_widget_map.Pointer(); for (RoutingIDWidgetMap::const_iterator it = widgets->begin(); it != widgets->end(); ++it) { - hosts.push_back(it->second); + hosts->Add(it->second); } - return hosts; + + return scoped_ptr<RenderWidgetHostIterator>(hosts); } // static @@ -358,8 +404,8 @@ void RenderWidgetHostImpl::SendScreenRects() { } base::TimeDelta - RenderWidgetHostImpl::GetSyntheticScrollMessageInterval() const { - return smooth_scroll_gesture_controller_.GetSyntheticScrollMessageInterval(); + RenderWidgetHostImpl::GetSyntheticGestureMessageInterval() const { + return synthetic_gesture_controller_.GetSyntheticGestureMessageInterval(); } void RenderWidgetHostImpl::SetOverscrollControllerEnabled(bool enabled) { @@ -430,6 +476,7 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect) IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateIsDelayed, OnUpdateIsDelayed) IPC_MESSAGE_HANDLER(ViewHostMsg_BeginSmoothScroll, OnBeginSmoothScroll) + IPC_MESSAGE_HANDLER(ViewHostMsg_BeginPinch, OnBeginPinch) IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnFocus) IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnBlur) IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnSetCursor) @@ -473,12 +520,15 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) { bool RenderWidgetHostImpl::Send(IPC::Message* msg) { if (IPC_MESSAGE_ID_CLASS(msg->type()) == InputMsgStart) - return input_router_->SendInput(msg); + return input_router_->SendInput(make_scoped_ptr(msg)); return process_->Send(msg); } void RenderWidgetHostImpl::WasHidden() { + if (is_hidden_) + return; + is_hidden_ = true; // Don't bother reporting hung state when we aren't active. @@ -499,7 +549,6 @@ void RenderWidgetHostImpl::WasHidden() { } void RenderWidgetHostImpl::WasShown() { - // When we create the widget, it is created as *not* hidden. if (!is_hidden_) return; is_hidden_ = false; @@ -548,8 +597,10 @@ void RenderWidgetHostImpl::WasShown() { } void RenderWidgetHostImpl::WasResized() { + // Skip if the |delegate_| has already been detached because + // it's web contents is being deleted. if (resize_ack_pending_ || !process_->HasConnection() || !view_ || - !renderer_initialized_ || should_auto_resize_) { + !renderer_initialized_ || should_auto_resize_ || !delegate_) { return; } @@ -565,7 +616,7 @@ void RenderWidgetHostImpl::WasResized() { bool size_changed = new_size != last_requested_size_; bool side_payload_changed = - !screen_info_.get() || + screen_info_out_of_date_ || old_physical_backing_size != physical_backing_size_ || was_fullscreen != is_fullscreen_ || old_overdraw_bottom_height != overdraw_bottom_height_; @@ -838,20 +889,20 @@ void RenderWidgetHostImpl::DonePaintingToBackingStore() { Send(new ViewMsg_UpdateRect_ACK(GetRoutingID())); } -void RenderWidgetHostImpl::ScheduleComposite() { +bool RenderWidgetHostImpl::ScheduleComposite() { if (is_hidden_ || !is_accelerated_compositing_active_ || - current_size_.IsEmpty()) { - return; + current_size_.IsEmpty() || repaint_ack_pending_ || + resize_ack_pending_ || view_being_painted_) { + return false; } // Send out a request to the renderer to paint the view if required. - if (!repaint_ack_pending_ && !resize_ack_pending_ && !view_being_painted_) { - repaint_start_time_ = TimeTicks::Now(); - repaint_ack_pending_ = true; - TRACE_EVENT_ASYNC_BEGIN0( - "renderer_host", "RenderWidgetHostImpl::repaint_ack_pending_", this); - Send(new ViewMsg_Repaint(routing_id_, current_size_)); - } + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + TRACE_EVENT_ASYNC_BEGIN0( + "renderer_host", "RenderWidgetHostImpl::repaint_ack_pending_", this); + Send(new ViewMsg_Repaint(routing_id_, current_size_)); + return true; } void RenderWidgetHostImpl::StartHangMonitorTimeout(TimeDelta delay) { @@ -1010,6 +1061,12 @@ void RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo( const MouseEventWithLatencyInfo& mouse_event) { TRACE_EVENT2("input", "RenderWidgetHostImpl::ForwardMouseEvent", "x", mouse_event.event.x, "y", mouse_event.event.y); + + for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) { + if (mouse_event_callbacks_[i].Run(mouse_event.event)) + return; + } + input_router_->SendMouseEvent(mouse_event); } @@ -1117,15 +1174,35 @@ ui::LatencyInfo RenderWidgetHostImpl::CreateRWHLatencyInfoIfNotExist( } -void RenderWidgetHostImpl::AddKeyboardListener(KeyboardListener* listener) { - keyboard_listeners_.AddObserver(listener); +void RenderWidgetHostImpl::AddKeyPressEventCallback( + const KeyPressEventCallback& callback) { + key_press_event_callbacks_.push_back(callback); +} + +void RenderWidgetHostImpl::RemoveKeyPressEventCallback( + const KeyPressEventCallback& callback) { + for (size_t i = 0; i < key_press_event_callbacks_.size(); ++i) { + if (key_press_event_callbacks_[i].Equals(callback)) { + key_press_event_callbacks_.erase( + key_press_event_callbacks_.begin() + i); + return; + } + } +} + +void RenderWidgetHostImpl::AddMouseEventCallback( + const MouseEventCallback& callback) { + mouse_event_callbacks_.push_back(callback); } -void RenderWidgetHostImpl::RemoveKeyboardListener( - KeyboardListener* listener) { - // Ensure that the element is actually an observer. - DCHECK(keyboard_listeners_.HasObserver(listener)); - keyboard_listeners_.RemoveObserver(listener); +void RenderWidgetHostImpl::RemoveMouseEventCallback( + const MouseEventCallback& callback) { + for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) { + if (mouse_event_callbacks_[i].Equals(callback)) { + mouse_event_callbacks_.erase(mouse_event_callbacks_.begin() + i); + return; + } + } } void RenderWidgetHostImpl::GetWebScreenInfo(WebKit::WebScreenInfo* result) { @@ -1134,6 +1211,7 @@ void RenderWidgetHostImpl::GetWebScreenInfo(WebKit::WebScreenInfo* result) { static_cast<RenderWidgetHostViewPort*>(GetView())->GetScreenInfo(result); else RenderWidgetHostViewPort::GetDefaultScreenInfo(result); + screen_info_out_of_date_ = false; } const NativeWebKeyboardEvent* @@ -1150,6 +1228,7 @@ void RenderWidgetHostImpl::NotifyScreenInfoChanged() { } void RenderWidgetHostImpl::InvalidateScreenInfo() { + screen_info_out_of_date_ = true; screen_info_.reset(); } @@ -1200,7 +1279,8 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status, waiting_for_screen_rects_ack_ = false; // Reset to ensure that input routing works with a new renderer. - input_router_.reset(new ImmediateInputRouter(process_, this, routing_id_)); + input_router_.reset( + new ImmediateInputRouter(process_, this, this, routing_id_)); if (overscroll_controller_) overscroll_controller_->Reset(); @@ -1262,7 +1342,7 @@ void RenderWidgetHostImpl::ImeSetComposition( void RenderWidgetHostImpl::ImeConfirmComposition( const string16& text, - const ui::Range& replacement_range, + const gfx::Range& replacement_range, bool keep_selection) { Send(new ViewMsg_ImeConfirmComposition( GetRoutingID(), text, replacement_range, keep_selection)); @@ -1482,7 +1562,9 @@ bool RenderWidgetHostImpl::OnSwapCompositorFrame( ack.gl_frame_data = frame->gl_frame_data.Pass(); ack.gl_frame_data->sync_point = 0; } else if (frame->delegated_frame_data) { - ack.resources.swap(frame->delegated_frame_data->resource_list); + cc::TransferableResource::ReturnResources( + frame->delegated_frame_data->resource_list, + &ack.resources); } else if (frame->software_frame_data) { ack.last_software_frame_id = frame->software_frame_data->id; } @@ -1665,7 +1747,14 @@ void RenderWidgetHostImpl::OnBeginSmoothScroll( const ViewHostMsg_BeginSmoothScroll_Params& params) { if (!view_) return; - smooth_scroll_gesture_controller_.BeginSmoothScroll(view_, params); + synthetic_gesture_controller_.BeginSmoothScroll(view_, params); +} + +void RenderWidgetHostImpl::OnBeginPinch( + const ViewHostMsg_BeginPinch_Params& params) { + if (!view_) + return; + synthetic_gesture_controller_.BeginPinch(view_, params); } void RenderWidgetHostImpl::OnFocus() { @@ -1689,15 +1778,15 @@ void RenderWidgetHostImpl::OnSetCursor(const WebCursor& cursor) { void RenderWidgetHostImpl::OnTextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (view_) - view_->TextInputTypeChanged(type, can_compose_inline, input_mode); + view_->TextInputTypeChanged(type, input_mode, can_compose_inline); } #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) void RenderWidgetHostImpl::OnImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { if (view_) view_->ImeCompositionRangeChanged(range, character_bounds); @@ -1879,10 +1968,8 @@ bool RenderWidgetHostImpl::KeyPressListenersHandleEvent( if (event.skip_in_browser || event.type != WebKeyboardEvent::RawKeyDown) return false; - ObserverList<KeyboardListener>::Iterator it(keyboard_listeners_); - KeyboardListener* listener; - while ((listener = it.GetNext()) != NULL) { - if (listener->HandleKeyPressEvent(event)) + for (size_t i = 0; i < key_press_event_callbacks_.size(); i++) { + if (key_press_event_callbacks_[i].Run(event)) return true; } @@ -2067,6 +2154,12 @@ bool RenderWidgetHostImpl::OnSendGestureEventImmediately( return !IgnoreInputEvents(); } +void RenderWidgetHostImpl::SetNeedsFlush() { +} + +void RenderWidgetHostImpl::DidFlush() { +} + void RenderWidgetHostImpl::OnKeyboardEventAck( const NativeWebKeyboardEvent& event, InputEventAckState ack_result) { @@ -2118,13 +2211,13 @@ void RenderWidgetHostImpl::OnTouchEventAck( view_->ProcessAckedTouchEvent(event, ack_result); } -void RenderWidgetHostImpl::OnUnexpectedEventAck(bool bad_message) { - if (bad_message) { +void RenderWidgetHostImpl::OnUnexpectedEventAck(UnexpectedEventAckType type) { + if (type == BAD_ACK_MESSAGE) { RecordAction(UserMetricsAction("BadMessageTerminate_RWH2")); process_->ReceivedBadMessage(); + } else if (type == UNEXPECTED_EVENT_TYPE) { + suppress_next_char_events_ = false; } - - suppress_next_char_events_ = false; } const gfx::Vector2d& RenderWidgetHostImpl::GetLastScrollOffset() const { @@ -2144,10 +2237,6 @@ bool RenderWidgetHostImpl::ShouldForwardGestureEvent( return input_router_->ShouldForwardGestureEvent(gesture_event); } -bool RenderWidgetHostImpl::HasQueuedGestureEvents() const { - return input_router_->HasQueuedGestureEvents(); -} - void RenderWidgetHostImpl::StartUserGesture() { OnUserGesture(); } @@ -2331,6 +2420,19 @@ void RenderWidgetHostImpl::SendSwapCompositorFrameAck( route_id, output_surface_id, ack)); } +// static +void RenderWidgetHostImpl::SendReclaimCompositorResources( + int32 route_id, + uint32 output_surface_id, + int renderer_host_id, + const cc::CompositorFrameAck& ack) { + RenderProcessHost* host = RenderProcessHost::FromID(renderer_host_id); + if (!host) + return; + host->Send( + new ViewMsg_ReclaimCompositorResources(route_id, output_surface_id, ack)); +} + void RenderWidgetHostImpl::AcknowledgeSwapBuffersToRenderer() { if (!is_threaded_compositing_enabled_) Send(new ViewMsg_SwapBuffers_ACK(routing_id_)); diff --git a/chromium/content/browser/renderer_host/render_widget_host_impl.h b/chromium/content/browser/renderer_host/render_widget_host_impl.h index 362c6d7f793..613177c9a81 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_impl.h +++ b/chromium/content/browser/renderer_host/render_widget_host_impl.h @@ -23,8 +23,9 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "build/build_config.h" +#include "content/browser/renderer_host/input/input_ack_handler.h" #include "content/browser/renderer_host/input/input_router_client.h" -#include "content/browser/renderer_host/smooth_scroll_gesture_controller.h" +#include "content/browser/renderer_host/synthetic_gesture_controller.h" #include "content/common/browser_rendering_stats.h" #include "content/common/view_message_enums.h" #include "content/port/browser/event_with_latency_info.h" @@ -34,7 +35,7 @@ #include "ipc/ipc_listener.h" #include "ui/base/ime/text_input_mode.h" #include "ui/base/ime/text_input_type.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #include "ui/gfx/native_widget_types.h" class WebCursor; @@ -53,9 +54,12 @@ class CompositorFrame; class CompositorFrameAck; } +namespace gfx { +class Range; +} + namespace ui { class KeyEvent; -class Range; } namespace WebKit { @@ -78,13 +82,14 @@ class MockRenderWidgetHost; class OverscrollController; class RenderWidgetHostDelegate; class RenderWidgetHostViewPort; -class SmoothScrollGestureController; +class SyntheticGestureController; struct EditCommand; // This implements the RenderWidgetHost interface that is exposed to // embedders of content, and adds things only visible to content. class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, public InputRouterClient, + public InputAckHandler, public IPC::Listener { public: // routing_id can be MSG_ROUTING_NONE, in which case the next available @@ -93,7 +98,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // |delegate| goes away. RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, - int routing_id); + int routing_id, + bool hidden); virtual ~RenderWidgetHostImpl(); // Similar to RenderWidgetHost::FromID, but returning the Impl object. @@ -102,14 +108,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // Returns all RenderWidgetHosts including swapped out ones for // internal use. The public interface // RendgerWidgetHost::GetRenderWidgetHosts only returns active ones. - // Keep in mind that there may be dependencies between these - // widgets. If a caller indirectly causes one of the widgets to be - // deleted while iterating over the list, the deleted widget will - // stay in the list and possibly causes a use-after-free. Take care - // to avoid deleting widgets as you iterate (e.g., see - // http://crbug.com/259859). TODO(nasko): Improve this interface to - // better prevent UaFs. - static std::vector<RenderWidgetHost*> GetAllRenderWidgetHosts(); + static scoped_ptr<RenderWidgetHostIterator> GetAllRenderWidgetHosts(); // Use RenderWidgetHostImpl::From(rwh) to downcast a // RenderWidgetHost to a RenderWidgetHostImpl. Internally, this @@ -172,8 +171,14 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, virtual void SetIgnoreInputEvents(bool ignore_input_events) OVERRIDE; virtual void Stop() OVERRIDE; virtual void WasResized() OVERRIDE; - virtual void AddKeyboardListener(KeyboardListener* listener) OVERRIDE; - virtual void RemoveKeyboardListener(KeyboardListener* listener) OVERRIDE; + virtual void AddKeyPressEventCallback( + const KeyPressEventCallback& callback) OVERRIDE; + virtual void RemoveKeyPressEventCallback( + const KeyPressEventCallback& callback) OVERRIDE; + virtual void AddMouseEventCallback( + const MouseEventCallback& callback) OVERRIDE; + virtual void RemoveMouseEventCallback( + const MouseEventCallback& callback) OVERRIDE; virtual void GetWebScreenInfo(WebKit::WebScreenInfo* result) OVERRIDE; virtual void GetSnapshotFromRenderer( const gfx::Rect& src_subrect, @@ -199,7 +204,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // Called when a renderer object already been created for this host, and we // just need to be attached to it. Used for window.open, <select> dropdown // menus, and other times when the renderer initiates creating an object. - void Init(); + virtual void Init(); // Tells the renderer to die and then calls Destroy(). virtual void Shutdown(); @@ -267,9 +272,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, void DonePaintingToBackingStore(); // GPU accelerated version of GetBackingStore function. This will - // trigger a re-composite to the view. If a resize is pending, it will - // block briefly waiting for an ack from the renderer. - void ScheduleComposite(); + // trigger a re-composite to the view. It may fail if a resize is pending, or + // if a composite has already been requested and not acked yet. + bool ScheduleComposite(); // Starts a hang monitor timeout. If there's already a hang monitor timeout // the new one will only fire if it has a shorter delay than the time @@ -335,7 +340,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // * when it receives a "commit" signal of GtkIMContext (on Linux); // * when insertText of NSTextInput is called (on Mac). void ImeConfirmComposition(const string16& text, - const ui::Range& replacement_range, + const gfx::Range& replacement_range, bool keep_selection); // Cancels an ongoing composition. @@ -365,7 +370,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, bool ShouldForwardTouchEvent() const; bool ShouldForwardGestureEvent( const GestureEventWithLatencyInfo& gesture_event) const; - bool HasQueuedGestureEvents() const; bool has_touch_handler() const { return has_touch_handler_; } @@ -463,6 +467,12 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, int renderer_host_id, const cc::CompositorFrameAck& ack); + // Called by the view to return resources to the compositor. + static void SendReclaimCompositorResources(int32 route_id, + uint32 output_surface_id, + int renderer_host_id, + const cc::CompositorFrameAck& ack); + // Called by the view in response to AcceleratedSurfaceBuffersSwapped for // platforms that support deferred GPU process descheduling. This does // nothing if the compositor thread is enabled. @@ -503,7 +513,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, return overscroll_controller_.get(); } - base::TimeDelta GetSyntheticScrollMessageInterval() const; + base::TimeDelta GetSyntheticGestureMessageInterval() const; // Sets whether the overscroll controller should be enabled for this page. void SetOverscrollControllerEnabled(bool enabled); @@ -648,15 +658,17 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, void OnUpdateIsDelayed(); void OnBeginSmoothScroll( const ViewHostMsg_BeginSmoothScroll_Params& params); + void OnBeginPinch( + const ViewHostMsg_BeginPinch_Params& params); virtual void OnFocus(); virtual void OnBlur(); void OnSetCursor(const WebCursor& cursor); void OnTextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode); + ui::TextInputMode input_mode, + bool can_compose_inline); #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) void OnImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds); #endif void OnImeCancelComposition(); @@ -729,6 +741,10 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, const TouchEventWithLatencyInfo& touch_event) OVERRIDE; virtual bool OnSendGestureEventImmediately( const GestureEventWithLatencyInfo& gesture_event) OVERRIDE; + virtual void SetNeedsFlush() OVERRIDE; + virtual void DidFlush() OVERRIDE; + + // InputAckHandler virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, InputEventAckState ack_result) OVERRIDE; virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event, @@ -737,7 +753,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, InputEventAckState ack_result) OVERRIDE; virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event, InputEventAckState ack_result) OVERRIDE; - virtual void OnUnexpectedEventAck(bool bad_message) OVERRIDE; + virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE; void SimulateTouchGestureWithMouse(const WebKit::WebMouseEvent& mouse_event); @@ -745,7 +761,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // which may get in recursive loops). void DelayedAutoResized(); - // Our delegate, which wants to know mainly about keyboard events. // It will remain non-NULL until DetachDelegate() is called. RenderWidgetHostDelegate* delegate_; @@ -821,7 +836,10 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, AccessibilityMode accessibility_mode_; // Keyboard event listeners. - ObserverList<KeyboardListener> keyboard_listeners_; + std::vector<KeyPressEventCallback> key_press_event_callbacks_; + + // Mouse event callbacks. + std::vector<MouseEventCallback> mouse_event_callbacks_; // If true, then we should repaint when restoring even if we have a // backingstore. This flag is set to true if we receive a paint message @@ -899,7 +917,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_; - SmoothScrollGestureController smooth_scroll_gesture_controller_; + SyntheticGestureController synthetic_gesture_controller_; // Receives and handles all input events. scoped_ptr<InputRouter> input_router_; diff --git a/chromium/content/browser/renderer_host/render_widget_host_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_unittest.cc index 960ce5eb92a..21c368ab488 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/basictypes.h" +#include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/memory/shared_memory.h" #include "base/timer/timer.h" @@ -28,7 +29,7 @@ #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/screen.h" @@ -40,7 +41,7 @@ #if defined(OS_WIN) || defined(USE_AURA) #include "content/browser/renderer_host/ui_events_helper.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" #endif using base::TimeDelta; @@ -104,30 +105,6 @@ class TestOverscrollDelegate : public OverscrollControllerDelegate { DISALLOW_COPY_AND_ASSIGN(TestOverscrollDelegate); }; -// MockKeyboardListener -------------------------------------------------------- -class MockKeyboardListener : public KeyboardListener { - public: - MockKeyboardListener() - : handle_key_press_event_(false) { - } - virtual ~MockKeyboardListener() {} - - // KeyboardListener: - virtual bool HandleKeyPressEvent( - const NativeWebKeyboardEvent& event) OVERRIDE { - return handle_key_press_event_; - } - - void set_handle_key_press_event(bool handle) { - handle_key_press_event_ = handle; - } - - private: - bool handle_key_press_event_; - - DISALLOW_COPY_AND_ASSIGN(MockKeyboardListener); -}; - // MockInputRouter ------------------------------------------------------------- class MockInputRouter : public InputRouter { @@ -145,12 +122,11 @@ class MockInputRouter : public InputRouter { virtual ~MockInputRouter() {} // InputRouter - virtual bool SendInput(IPC::Message* message) OVERRIDE { + virtual void Flush() OVERRIDE { + flush_called_ = true; + } + virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE { send_event_called_ = true; - - // SendInput takes ownership of message - delete message; - return true; } virtual void SendMouseEvent( @@ -195,7 +171,6 @@ class MockInputRouter : public InputRouter { const GestureEventWithLatencyInfo& gesture_event) const OVERRIDE { return true; } - virtual bool HasQueuedGestureEvents() const OVERRIDE { return true; } // IPC::Listener virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { @@ -203,6 +178,7 @@ class MockInputRouter : public InputRouter { return false; } + bool flush_called_; bool send_event_called_; bool sent_mouse_event_; bool sent_wheel_event_; @@ -225,7 +201,7 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl { RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id), + : RenderWidgetHostImpl(delegate, process, routing_id, false), unresponsive_timer_fired_(false) { immediate_input_router_ = static_cast<ImmediateInputRouter*>(input_router_.get()); @@ -628,11 +604,21 @@ class MockPaintingObserver : public NotificationObserver { class RenderWidgetHostTest : public testing::Test { public: - RenderWidgetHostTest() : process_(NULL) { + RenderWidgetHostTest() + : process_(NULL), + handle_key_press_event_(false), + handle_mouse_event_(false) { } virtual ~RenderWidgetHostTest() { } + bool KeyPressEventCallback(const NativeWebKeyboardEvent& /* event */) { + return handle_key_press_event_; + } + bool MouseEventCallback(const WebKit::WebMouseEvent& /* event */) { + return handle_mouse_event_; + } + protected: // testing::Test virtual void SetUp() { @@ -835,6 +821,8 @@ class RenderWidgetHostTest : public testing::Test { scoped_ptr<MockRenderWidgetHost> host_; scoped_ptr<TestView> view_; scoped_ptr<gfx::Screen> screen_; + bool handle_key_press_event_; + bool handle_mouse_event_; private: WebTouchEvent touch_event_; @@ -857,13 +845,12 @@ class RenderWidgetHostWithSourceTest // ----------------------------------------------------------------------------- TEST_F(RenderWidgetHostTest, Resize) { - // The initial bounds is the empty rect, but the screen info hasn't been sent - // yet, so setting it to the same thing should send the resize message. + // The initial bounds is the empty rect, and the screen info hasn't been sent + // yet, so setting it to the same thing shouldn't send the resize message. view_->set_bounds(gfx::Rect()); host_->WasResized(); EXPECT_FALSE(host_->resize_ack_pending_); - EXPECT_EQ(gfx::Size(), host_->last_requested_size_); - EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); + EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); // Setting the bounds to a "real" rect should send out the notification. // but should not expect ack for empty physical backing size. @@ -1397,6 +1384,7 @@ TEST_F(RenderWidgetHostTest, WheelScrollEventOverscrolls) { SimulateMouseMove(5, 10, 0); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, process_->sink().message_count()); } // Tests that if some scroll events are consumed towards the start, then @@ -1618,10 +1606,10 @@ TEST_F(RenderWidgetHostTest, ScrollEventsOverscrollWithZeroFling) { // Send a fling start, but with a small velocity, so that the overscroll is // aborted. The fling should proceed to the renderer, through the gesture // event filter. - SimulateGestureFlingStartEvent(0.f, 0.f, WebGestureEvent::Touchpad); + SimulateGestureFlingStartEvent(10.f, 0.f, WebGestureEvent::Touchpad); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); - EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); - EXPECT_EQ(0U, process_->sink().message_count()); + EXPECT_EQ(1U, host_->GestureEventLastQueueEventSize()); + EXPECT_EQ(1U, process_->sink().message_count()); } // Tests that a fling in the opposite direction of the overscroll cancels the @@ -1646,11 +1634,13 @@ TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) { INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->current_mode()); + process_->sink().ClearMessages(); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, process_->sink().message_count()); SendInputEventACK(WebInputEvent::GestureScrollEnd, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); } @@ -1670,9 +1660,12 @@ TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) { INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_delegate()->current_mode()); + process_->sink().ClearMessages(); + SimulateGestureFlingStartEvent(100, 0, WebGestureEvent::Touchscreen); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->completed_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, process_->sink().message_count()); } } @@ -1750,11 +1743,14 @@ TEST_F(RenderWidgetHostTest, GestureScrollConsumedHorizontal) { INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); + process_->sink().ClearMessages(); // Send another gesture event and ACK as not being processed. This should // not initiate overscroll because the beginning of the scroll event did - // scroll some content on the page. + // scroll some content on the page. Since there was no overscroll, the event + // should reach the renderer. SimulateGestureScrollUpdateEvent(55, 0, 0); + EXPECT_EQ(1U, process_->sink().message_count()); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); @@ -2301,12 +2297,15 @@ TEST_F(RenderWidgetHostTest, OverscrollMouseMoveCompletion) { // Overscroll gesture is in progress. Send a mouse-move now. This should // complete the gesture (because the amount overscrolled is above the - // threshold), and consume the event. + // threshold). SimulateMouseMove(5, 10, 0); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); - EXPECT_EQ(0U, process_->sink().message_count()); + EXPECT_EQ(1U, process_->sink().message_count()); + process_->sink().ClearMessages(); + SendInputEventACK(WebInputEvent::MouseMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen); @@ -2499,32 +2498,30 @@ TEST_F(RenderWidgetHostTest, IgnoreInputEvent) { TEST_F(RenderWidgetHostTest, KeyboardListenerIgnoresEvent) { host_->SetupForInputRouterTest(); - - scoped_ptr<MockKeyboardListener> keyboard_listener_(new MockKeyboardListener); - host_->AddKeyboardListener(keyboard_listener_.get()); - - keyboard_listener_->set_handle_key_press_event(false); + host_->AddKeyPressEventCallback( + base::Bind(&RenderWidgetHostTest::KeyPressEventCallback, + base::Unretained(this))); + handle_key_press_event_ = false; SimulateKeyboardEvent(WebInputEvent::RawKeyDown); EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); - - host_->RemoveKeyboardListener(keyboard_listener_.get()); } TEST_F(RenderWidgetHostTest, KeyboardListenerSuppressFollowingEvents) { host_->SetupForInputRouterTest(); - scoped_ptr<MockKeyboardListener> keyboard_listener_(new MockKeyboardListener); - host_->AddKeyboardListener(keyboard_listener_.get()); + host_->AddKeyPressEventCallback( + base::Bind(&RenderWidgetHostTest::KeyPressEventCallback, + base::Unretained(this))); - // KeyboardListener handles the first event - keyboard_listener_->set_handle_key_press_event(true); + // The callback handles the first event + handle_key_press_event_ = true; SimulateKeyboardEvent(WebInputEvent::RawKeyDown); EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); // Following Char events should be suppressed - keyboard_listener_->set_handle_key_press_event(false); + handle_key_press_event_ = false; SimulateKeyboardEvent(WebInputEvent::Char); EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); SimulateKeyboardEvent(WebInputEvent::Char); @@ -2537,8 +2534,24 @@ TEST_F(RenderWidgetHostTest, KeyboardListenerSuppressFollowingEvents) { host_->mock_input_router()->sent_keyboard_event_ = false; SimulateKeyboardEvent(WebInputEvent::Char); EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); +} + +TEST_F(RenderWidgetHostTest, MouseEventCallbackCanHandleEvent) { + host_->SetupForInputRouterTest(); + + host_->AddMouseEventCallback( + base::Bind(&RenderWidgetHostTest::MouseEventCallback, + base::Unretained(this))); + + handle_mouse_event_ = true; + SimulateMouseEvent(WebInputEvent::MouseDown); + + EXPECT_FALSE(host_->mock_input_router()->sent_mouse_event_); + + handle_mouse_event_ = false; + SimulateMouseEvent(WebInputEvent::MouseDown); - host_->RemoveKeyboardListener(keyboard_listener_.get()); + EXPECT_TRUE(host_->mock_input_router()->sent_mouse_event_); } TEST_F(RenderWidgetHostTest, InputRouterReceivesHandleInputEvent_ACK) { diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_android.cc b/chromium/content/browser/renderer_host/render_widget_host_view_android.cc index 8e885407e5c..3b94566748f 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_android.cc @@ -6,8 +6,9 @@ #include <android/bitmap.h> +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" @@ -20,6 +21,7 @@ #include "cc/output/compositor_frame_ack.h" #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" +#include "cc/resources/single_release_callback.h" #include "cc/trees/layer_tree_host.h" #include "content/browser/accessibility/browser_accessibility_manager_android.h" #include "content/browser/android/content_view_core_impl.h" @@ -28,10 +30,10 @@ #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/browser/renderer_host/compositor_impl_android.h" #include "content/browser/renderer_host/dip_util.h" +#include "content/browser/renderer_host/generic_touch_gesture_android.h" #include "content/browser/renderer_host/image_transport_factory_android.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/renderer_host/surface_texture_transport_client_android.h" -#include "content/browser/renderer_host/touch_smooth_scroll_gesture_android.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/gpu_messages.h" #include "content/common/input_messages.h" @@ -51,6 +53,7 @@ namespace content { namespace { const int kUndefinedOutputSurfaceId = -1; +const int kMinimumPointerDistance = 50; void InsertSyncPointAndAckForGpu( int gpu_host_id, int route_id, const std::string& return_mailbox) { @@ -88,12 +91,12 @@ void SendImeEventAck(RenderWidgetHostImpl* host) { void CopyFromCompositingSurfaceFinished( const base::Callback<void(bool, const SkBitmap&)>& callback, - const cc::TextureMailbox::ReleaseCallback& release_callback, + scoped_ptr<cc::SingleReleaseCallback> release_callback, scoped_ptr<SkBitmap> bitmap, scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock, bool result) { bitmap_pixels_lock.reset(); - release_callback.Run(0, false); + release_callback->Run(0, false); callback.Run(result, *bitmap); } @@ -196,7 +199,7 @@ RenderWidgetHostViewAndroid::GetRenderWidgetHost() const { } void RenderWidgetHostViewAndroid::WasShown() { - if (!host_->is_hidden()) + if (!host_ || !host_->is_hidden()) return; host_->WasShown(); @@ -205,7 +208,7 @@ void RenderWidgetHostViewAndroid::WasShown() { void RenderWidgetHostViewAndroid::WasHidden() { RunAckCallbacks(); - if (host_->is_hidden()) + if (!host_ || host_->is_hidden()) return; // Inform the renderer that we are being hidden so it can reduce its resource @@ -353,6 +356,8 @@ void RenderWidgetHostViewAndroid::Show() { are_layers_attached_ = true; AttachLayers(); + + WasShown(); } void RenderWidgetHostViewAndroid::Hide() { @@ -361,6 +366,8 @@ void RenderWidgetHostViewAndroid::Hide() { are_layers_attached_ = false; RemoveLayers(); + + WasHidden(); } bool RenderWidgetHostViewAndroid::IsShowing() { @@ -406,8 +413,8 @@ void RenderWidgetHostViewAndroid::SetIsLoading(bool is_loading) { void RenderWidgetHostViewAndroid::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { // Unused on Android, which uses OnTextInputChanged instead. } @@ -420,9 +427,9 @@ void RenderWidgetHostViewAndroid::OnTextInputStateChanged( // If an acknowledgement is required for this event, regardless of how we exit // from this method, we must acknowledge that we processed the input state // change. - base::ScopedClosureRunner ack_caller(base::Bind(&SendImeEventAck, host_)); - if (!params.require_ack) - ack_caller.Release(); + base::ScopedClosureRunner ack_caller; + if (params.require_ack) + ack_caller.Reset(base::Bind(&SendImeEventAck, host_)); if (!IsShowing()) return; @@ -510,7 +517,7 @@ void RenderWidgetHostViewAndroid::SetTooltipText( void RenderWidgetHostViewAndroid::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { RenderWidgetHostViewBase::SelectionChanged(text, offset, range); if (text.empty() || range.is_empty() || !content_view_core_) @@ -606,14 +613,28 @@ void RenderWidgetHostViewAndroid::ShowDisambiguationPopup( content_view_core_->ShowDisambiguationPopup(target_rect, zoomed_bitmap); } -SmoothScrollGesture* RenderWidgetHostViewAndroid::CreateSmoothScrollGesture( +SyntheticGesture* RenderWidgetHostViewAndroid::CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) { - return new TouchSmoothScrollGestureAndroid( - pixels_to_scroll, + return new GenericTouchGestureAndroid( + GetRenderWidgetHost(), + content_view_core_->CreateOnePointTouchGesture( + mouse_event_x, mouse_event_y, + 0, scroll_down ? -pixels_to_scroll : pixels_to_scroll)); +} + +SyntheticGesture* RenderWidgetHostViewAndroid::CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) { + int distance_between_pointers = zoom_in ? + kMinimumPointerDistance : (kMinimumPointerDistance + pixels_to_move); + return new GenericTouchGestureAndroid( GetRenderWidgetHost(), - content_view_core_->CreateSmoothScroller( - scroll_down, mouse_event_x, mouse_event_y)); + content_view_core_->CreateTwoPointTouchGesture( + anchor_x, anchor_y - distance_between_pointers / 2, + 0, (zoom_in ? -pixels_to_move : pixels_to_move) / 2, + anchor_x, anchor_y + distance_between_pointers / 2, + 0, (zoom_in ? pixels_to_move : -pixels_to_move) / 2)); } void RenderWidgetHostViewAndroid::OnAcceleratedCompositingStateChange() { @@ -828,7 +849,7 @@ void RenderWidgetHostViewAndroid::CreateOverscrollEffectIfNecessary() { if (!overscroll_effect_enabled_ || overscroll_effect_) return; - overscroll_effect_ = OverscrollGlow::Create(true); + overscroll_effect_ = OverscrollGlow::Create(true, content_size_in_layer_); // Prevent future creation attempts on failure. if (!overscroll_effect_) @@ -950,8 +971,8 @@ InputEventAckState RenderWidgetHostViewAndroid::FilterInputEvent( return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; } -void RenderWidgetHostViewAndroid::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewAndroid::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (!host_ || host_->accessibility_mode() != AccessibilityModeComplete || !content_view_core_) { @@ -965,7 +986,7 @@ void RenderWidgetHostViewAndroid::OnAccessibilityNotifications( BrowserAccessibilityManagerAndroid::GetEmptyDocument(), this)); } - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } void RenderWidgetHostViewAndroid::SetAccessibilityFocus(int acc_obj_id) { @@ -1183,6 +1204,7 @@ WebKit::WebGraphicsContext3D* RenderWidgetHostViewAndroid::Context3d() { bool RenderWidgetHostViewAndroid::PrepareTextureMailbox( cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* release_callback, bool use_shared_memory) { return false; } @@ -1223,23 +1245,25 @@ void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult( new SkAutoLockPixels(*bitmap)); uint8* pixels = static_cast<uint8*>(bitmap->getPixels()); - scoped_ptr<cc::TextureMailbox> texture_mailbox = result->TakeTexture(); - DCHECK(texture_mailbox->IsTexture()); - if (!texture_mailbox->IsTexture()) + cc::TextureMailbox texture_mailbox; + scoped_ptr<cc::SingleReleaseCallback> release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox->name(), - texture_mailbox->sync_point(), + texture_mailbox.name(), + texture_mailbox.sync_point(), result->size(), gfx::Rect(result->size()), dst_size_in_pixel, pixels, base::Bind(&CopyFromCompositingSurfaceFinished, callback, - texture_mailbox->callback(), + base::Passed(&release_callback), base::Passed(&bitmap), base::Passed(&bitmap_pixels_lock))); } @@ -1264,7 +1288,7 @@ void RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult( DCHECK_EQ(source->width(), dst_size_in_pixel.width()); DCHECK_EQ(source->height(), dst_size_in_pixel.height()); - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); callback.Run(true, *source); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_android.h b/chromium/content/browser/renderer_host/render_widget_host_view_android.h index d8ea83da7ce..6f248076e97 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_android.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_android.h @@ -36,6 +36,7 @@ namespace cc { class CopyOutputResult; class DelegatedRendererLayer; class Layer; +class SingleReleaseCallback; class TextureLayer; } @@ -98,8 +99,8 @@ class RenderWidgetHostViewAndroid virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -112,7 +113,7 @@ class RenderWidgetHostViewAndroid virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -152,8 +153,8 @@ class RenderWidgetHostViewAndroid const WebKit::WebInputEvent& input_event) OVERRIDE; virtual void GestureEventAck(int gesture_event_type, InputEventAckState ack_result) OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; @@ -165,9 +166,12 @@ class RenderWidgetHostViewAndroid gfx::Vector2dF current_fling_velocity) OVERRIDE; virtual void ShowDisambiguationPopup(const gfx::Rect& target_rect, const SkBitmap& zoomed_bitmap) OVERRIDE; - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) OVERRIDE; + virtual SyntheticGesture* CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) OVERRIDE; // Implementation of BrowserAccessibilityDelegate: virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE; @@ -184,8 +188,10 @@ class RenderWidgetHostViewAndroid // cc::TextureLayerClient implementation. virtual unsigned PrepareTexture() OVERRIDE; virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE; - virtual bool PrepareTextureMailbox(cc::TextureMailbox* mailbox, - bool use_shared_memory) OVERRIDE; + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE; // cc::DelegatedRendererLayerClient implementation. virtual void DidCommitFrameData() OVERRIDE; diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc b/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc index 33ee9524e73..2eb34b98d38 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -4,8 +4,9 @@ #include "content/browser/renderer_host/render_widget_host_view_aura.h" +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/logging.h" @@ -16,8 +17,11 @@ #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" #include "cc/resources/texture_mailbox.h" +#include "cc/trees/layer_tree_settings.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "content/browser/aura/compositor_resize_lock.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/renderer_host/backing_store_aura.h" #include "content/browser/renderer_host/dip_util.h" #include "content/browser/renderer_host/overscroll_controller.h" @@ -57,13 +61,13 @@ #include "ui/aura/window_observer.h" #include "ui/aura/window_tracker.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" #include "ui/base/gestures/gesture_recognizer.h" #include "ui/base/hit_test.h" #include "ui/base/ime/input_method.h" #include "ui/base/ui_base_types.h" #include "ui/compositor/layer.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/gfx/canvas.h" #include "ui/gfx/display.h" #include "ui/gfx/rect_conversions.h" @@ -77,6 +81,7 @@ #include "content/browser/accessibility/browser_accessibility_win.h" #include "ui/base/win/hidden_window.h" #include "ui/gfx/gdi_util.h" +#include "ui/gfx/win/dpi.h" #endif using gfx::RectToSkIRect; @@ -100,10 +105,10 @@ class MemoryHolder : public base::RefCounted<MemoryHolder> { frame_size_(frame_size), callback_(callback) {} - cc::TextureMailbox GetMailbox() { - return cc::TextureMailbox( - shared_memory_.get(), - frame_size_, + void GetMailbox(cc::TextureMailbox* mailbox, + scoped_ptr<cc::SingleReleaseCallback>* release_callback) { + *mailbox = cc::TextureMailbox(shared_memory_.get(), frame_size_); + *release_callback = cc::SingleReleaseCallback::Create( base::Bind(ReleaseMailbox, make_scoped_refptr(this))); } @@ -118,6 +123,11 @@ class MemoryHolder : public base::RefCounted<MemoryHolder> { namespace { +void MailboxReleaseCallback(scoped_ptr<base::SharedMemory> shared_memory, + unsigned sync_point, bool lost_resource) { + // NOTE: shared_memory will get released when we go out of scope. +} + // In mouse lock mode, we need to prevent the (invisible) cursor from hitting // the border of the view, in order to get valid movement information. However, // forcing the cursor back to the center of the view after each mouse move @@ -567,73 +577,6 @@ class RenderWidgetHostViewAura::TransientWindowObserver #endif -class RenderWidgetHostViewAura::ResizeLock { - public: - ResizeLock(aura::RootWindow* root_window, - const gfx::Size new_size, - bool defer_compositor_lock) - : root_window_(root_window), - new_size_(new_size), - compositor_lock_(defer_compositor_lock ? - NULL : - root_window_->compositor()->GetCompositorLock()), - weak_ptr_factory_(this), - defer_compositor_lock_(defer_compositor_lock) { - TRACE_EVENT_ASYNC_BEGIN2("ui", "ResizeLock", this, - "width", new_size_.width(), - "height", new_size_.height()); - root_window_->HoldPointerMoves(); - - BrowserThread::PostDelayedTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&RenderWidgetHostViewAura::ResizeLock::CancelLock, - weak_ptr_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kResizeLockTimeoutMs)); - } - - ~ResizeLock() { - CancelLock(); - TRACE_EVENT_ASYNC_END2("ui", "ResizeLock", this, - "width", new_size_.width(), - "height", new_size_.height()); - } - - void UnlockCompositor() { - defer_compositor_lock_ = false; - compositor_lock_ = NULL; - } - - void CancelLock() { - if (!root_window_) - return; - UnlockCompositor(); - root_window_->ReleasePointerMoves(); - root_window_ = NULL; - } - - const gfx::Size& expected_size() const { - return new_size_; - } - - bool GrabDeferredLock() { - if (root_window_ && defer_compositor_lock_) { - compositor_lock_ = root_window_->compositor()->GetCompositorLock(); - defer_compositor_lock_ = false; - return true; - } - return false; - } - - private: - aura::RootWindow* root_window_; - gfx::Size new_size_; - scoped_refptr<ui::CompositorLock> compositor_lock_; - base::WeakPtrFactory<ResizeLock> weak_ptr_factory_; - bool defer_compositor_lock_; - - DISALLOW_COPY_AND_ASSIGN(ResizeLock); -}; - //////////////////////////////////////////////////////////////////////////////// // RenderWidgetHostViewAura, public: @@ -646,8 +589,11 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host) popup_child_host_view_(NULL), is_loading_(false), text_input_type_(ui::TEXT_INPUT_TYPE_NONE), + text_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT), can_compose_inline_(true), has_composition_text_(false), + last_output_surface_id_(0), + skipped_frames_(false), last_swapped_surface_scale_factor_(1.f), paint_canvas_(NULL), synthetic_move_sent_(false), @@ -746,18 +692,26 @@ RenderWidgetHost* RenderWidgetHostViewAura::GetRenderWidgetHost() const { } void RenderWidgetHostViewAura::WasShown() { + DCHECK(host_); if (!host_->is_hidden()) return; host_->WasShown(); + if (framebuffer_holder_) + FrameMemoryManager::GetInstance()->SetFrameVisibility(this, true); - aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(window_->GetRootWindow()); - if (cursor_client) - NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible()); + aura::RootWindow* root = window_->GetRootWindow(); + if (root) { + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(root); + if (cursor_client) + NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible()); + } if (!current_surface_.get() && host_->is_accelerated_compositing_active() && !released_front_lock_.get()) { - released_front_lock_ = GetCompositor()->GetCompositorLock(); + ui::Compositor* compositor = GetCompositor(); + if (compositor) + released_front_lock_ = compositor->GetCompositorLock(); } #if defined(OS_WIN) @@ -768,9 +722,11 @@ void RenderWidgetHostViewAura::WasShown() { } void RenderWidgetHostViewAura::WasHidden() { - if (host_->is_hidden()) + if (!host_ || host_->is_hidden()) return; host_->WasHidden(); + if (framebuffer_holder_) + FrameMemoryManager::GetInstance()->SetFrameVisibility(this, false); released_front_lock_ = NULL; @@ -803,39 +759,69 @@ void RenderWidgetHostViewAura::SetBounds(const gfx::Rect& rect) { } void RenderWidgetHostViewAura::MaybeCreateResizeLock() { - gfx::Size desired_size = window_->bounds().size(); - if (!host_->should_auto_resize() && - !resize_lock_.get() && - desired_size != current_frame_size_ && - host_->is_accelerated_compositing_active()) { - aura::RootWindow* root_window = window_->GetRootWindow(); - ui::Compositor* compositor = root_window ? - root_window->compositor() : NULL; - if (root_window && compositor) { - // Listen to changes in the compositor lock state. - if (!compositor->HasObserver(this)) - compositor->AddObserver(this); - -// On Windows while resizing, the the resize locks makes us mis-paint a white -// vertical strip (including the non-client area) if the content composition is -// lagging the UI composition. So here we disable the throttling so that the UI -// bits can draw ahead of the content thereby reducing the amount of whiteout. -// Because this causes the content to be drawn at wrong sizes while resizing -// we compensate by blocking the UI thread in Compositor::Draw() by issuing a -// FinishAllRendering() if we are resizing. -#if !defined (OS_WIN) - bool defer_compositor_lock = - can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || - can_lock_compositor_ == NO_PENDING_COMMIT; - - if (can_lock_compositor_ == YES) - can_lock_compositor_ = YES_DID_LOCK; - - resize_lock_.reset(new ResizeLock(root_window, desired_size, - defer_compositor_lock)); + if (!ShouldCreateResizeLock()) + return; + DCHECK(window_->GetRootWindow()); + DCHECK(window_->GetRootWindow()->compositor()); + + // Listen to changes in the compositor lock state. + ui::Compositor* compositor = window_->GetRootWindow()->compositor(); + if (!compositor->HasObserver(this)) + compositor->AddObserver(this); + + bool defer_compositor_lock = + can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || + can_lock_compositor_ == NO_PENDING_COMMIT; + + if (can_lock_compositor_ == YES) + can_lock_compositor_ = YES_DID_LOCK; + + resize_lock_ = CreateResizeLock(defer_compositor_lock); +} + +bool RenderWidgetHostViewAura::ShouldCreateResizeLock() { + // On Windows while resizing, the the resize locks makes us mis-paint a white + // vertical strip (including the non-client area) if the content composition + // is lagging the UI composition. So here we disable the throttling so that + // the UI bits can draw ahead of the content thereby reducing the amount of + // whiteout. Because this causes the content to be drawn at wrong sizes while + // resizing we compensate by blocking the UI thread in Compositor::Draw() by + // issuing a FinishAllRendering() if we are resizing. +#if defined (OS_WIN) + return false; #endif - } - } + + if (resize_lock_) + return false; + + if (host_->should_auto_resize()) + return false; + if (!host_->is_accelerated_compositing_active()) + return false; + + gfx::Size desired_size = window_->bounds().size(); + if (desired_size == current_frame_size_) + return false; + + aura::RootWindow* root_window = window_->GetRootWindow(); + if (!root_window) + return false; + + ui::Compositor* compositor = root_window->compositor(); + if (!compositor) + return false; + + return true; +} + +scoped_ptr<ResizeLock> RenderWidgetHostViewAura::CreateResizeLock( + bool defer_compositor_lock) { + gfx::Size desired_size = window_->bounds().size(); + return scoped_ptr<ResizeLock>(new CompositorResizeLock( + window_->GetRootWindow(), + desired_size, + defer_compositor_lock, + base::TimeDelta::FromMilliseconds(kResizeLockTimeoutMs))); } gfx::NativeView RenderWidgetHostViewAura::GetNativeView() const { @@ -983,10 +969,12 @@ bool RenderWidgetHostViewAura::IsSurfaceAvailableForCopy() const { void RenderWidgetHostViewAura::Show() { window_->Show(); + WasShown(); } void RenderWidgetHostViewAura::Hide() { window_->Hide(); + WasHidden(); } bool RenderWidgetHostViewAura::IsShowing() { @@ -1010,18 +998,6 @@ void RenderWidgetHostViewAura::SetBackground(const SkBitmap& background) { window_->layer()->SetFillsBoundsOpaquely(background.isOpaque()); } -#if defined(OS_WIN) -gfx::NativeViewAccessible -RenderWidgetHostViewAura::AccessibleObjectFromChildId(long child_id) { - BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager(); - if (!manager) - return NULL; - - return manager->ToBrowserAccessibilityManagerWin()->GetFromUniqueIdWin( - child_id); -} -#endif // defined(OS_WIN) - void RenderWidgetHostViewAura::UpdateCursor(const WebCursor& cursor) { current_cursor_ = cursor; const gfx::Display display = gfx::Screen::GetScreenFor(window_)-> @@ -1039,11 +1015,13 @@ void RenderWidgetHostViewAura::SetIsLoading(bool is_loading) { void RenderWidgetHostViewAura::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (text_input_type_ != type || + text_input_mode_ != input_mode || can_compose_inline_ != can_compose_inline) { text_input_type_ = type; + text_input_mode_ = input_mode; can_compose_inline_ = can_compose_inline; if (GetInputMethod()) GetInputMethod()->OnTextInputTypeChanged(this); @@ -1059,7 +1037,7 @@ void RenderWidgetHostViewAura::ImeCancelComposition() { } void RenderWidgetHostViewAura::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { composition_character_bounds_ = character_bounds; } @@ -1144,7 +1122,7 @@ void RenderWidgetHostViewAura::SetTooltipText(const string16& tooltip_text) { void RenderWidgetHostViewAura::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { RenderWidgetHostViewBase::SelectionChanged(text, offset, range); #if defined(USE_X11) && !defined(OS_CHROMEOS) @@ -1308,9 +1286,14 @@ void RenderWidgetHostViewAura::UpdateExternalTexture() { current_frame_size_ = ConvertSizeToDIP( current_surface_->device_scale_factor(), current_surface_->size()); CheckResizeLock(); + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); } else if (is_compositing_active && framebuffer_holder_) { - cc::TextureMailbox mailbox = framebuffer_holder_->GetMailbox(); + cc::TextureMailbox mailbox; + scoped_ptr<cc::SingleReleaseCallback> callback; + framebuffer_holder_->GetMailbox(&mailbox, &callback); window_->layer()->SetTextureMailbox(mailbox, + callback.Pass(), last_swapped_surface_scale_factor_); current_frame_size_ = ConvertSizeToDIP(last_swapped_surface_scale_factor_, mailbox.shared_memory_size()); @@ -1319,6 +1302,8 @@ void RenderWidgetHostViewAura::UpdateExternalTexture() { window_->layer()->SetExternalTexture(NULL); resize_lock_.reset(); host_->WasResized(); + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); } } @@ -1440,20 +1425,58 @@ void RenderWidgetHostViewAura::SwapDelegatedFrame( scoped_ptr<cc::DelegatedFrameData> frame_data, float frame_device_scale_factor, const ui::LatencyInfo& latency_info) { + gfx::Size frame_size; gfx::Size frame_size_in_dip; + gfx::Rect damage_rect; + gfx::Rect damage_rect_in_dip; + if (!frame_data->render_pass_list.empty()) { - frame_size_in_dip = gfx::ToFlooredSize(gfx::ScaleSize( - frame_data->render_pass_list.back()->output_rect.size(), - 1.f/frame_device_scale_factor)); + cc::RenderPass* root_pass = frame_data->render_pass_list.back(); + + frame_size = root_pass->output_rect.size(); + frame_size_in_dip = ConvertSizeToDIP(frame_device_scale_factor, frame_size); + + damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect); + damage_rect.Intersect(gfx::Rect(frame_size)); + damage_rect_in_dip = ConvertRectToDIP(frame_device_scale_factor, + damage_rect); } + + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); + if (ShouldSkipFrame(frame_size_in_dip)) { cc::CompositorFrameAck ack; - ack.resources.swap(frame_data->resource_list); + cc::TransferableResource::ReturnResources(frame_data->resource_list, + &ack.resources); RenderWidgetHostImpl::SendSwapCompositorFrameAck( host_->GetRoutingID(), output_surface_id, host_->GetProcess()->GetID(), ack); + skipped_frames_ = true; return; } + + if (skipped_frames_) { + skipped_frames_ = false; + damage_rect = gfx::Rect(frame_size); + damage_rect_in_dip = gfx::Rect(frame_size_in_dip); + + // Give the same damage rect to the compositor. + cc::RenderPass* root_pass = frame_data->render_pass_list.back(); + root_pass->damage_rect = damage_rect; + } + + if (output_surface_id != last_output_surface_id_) { + // Resource ids are scoped by the output surface. + // If the originating output surface doesn't match the last one, it + // indicates the renderer's output surface may have been recreated, in which + // case we should recreate the DelegatedRendererLayer, to avoid matching + // resources from the old one with resources from the new one which would + // have the same id. + window_->layer()->SetDelegatedFrame(scoped_ptr<cc::DelegatedFrameData>(), + frame_size_in_dip); + last_output_surface_id_ = output_surface_id; + } window_->layer()->SetDelegatedFrame(frame_data.Pass(), frame_size_in_dip); released_front_lock_ = NULL; current_frame_size_ = frame_size_in_dip; @@ -1461,6 +1484,7 @@ void RenderWidgetHostViewAura::SwapDelegatedFrame( if (paint_observer_) paint_observer_->OnUpdateCompositorContent(); + window_->SchedulePaintInRect(damage_rect_in_dip); ui::Compositor* compositor = GetCompositor(); if (!compositor) { @@ -1493,7 +1517,8 @@ void RenderWidgetHostViewAura::SwapSoftwareFrame( gfx::Size frame_size_in_dip = ConvertSizeToDIP(frame_device_scale_factor, frame_size); if (ShouldSkipFrame(frame_size_in_dip)) { - SendSoftwareFrameAck(output_surface_id, frame_data->id); + ReleaseSoftwareFrame(output_surface_id, frame_data->id); + SendSoftwareFrameAck(output_surface_id); return; } @@ -1522,41 +1547,83 @@ void RenderWidgetHostViewAura::SwapSoftwareFrame( scoped_refptr<MemoryHolder> holder(new MemoryHolder( shared_memory.Pass(), frame_size, - base::Bind(&RenderWidgetHostViewAura::SendSoftwareFrameAck, + base::Bind(&RenderWidgetHostViewAura::ReleaseSoftwareFrame, AsWeakPtr(), output_surface_id, frame_data->id))); framebuffer_holder_.swap(holder); - cc::TextureMailbox mailbox = framebuffer_holder_->GetMailbox(); + cc::TextureMailbox mailbox; + scoped_ptr<cc::SingleReleaseCallback> callback; + framebuffer_holder_->GetMailbox(&mailbox, &callback); DCHECK(mailbox.IsSharedMemory()); current_frame_size_ = frame_size_in_dip; released_front_lock_ = NULL; CheckResizeLock(); - window_->layer()->SetTextureMailbox(mailbox, frame_device_scale_factor); + window_->layer()->SetTextureMailbox(mailbox, + callback.Pass(), + frame_device_scale_factor); window_->SchedulePaintInRect( ConvertRectToDIP(frame_device_scale_factor, damage_rect)); ui::Compositor* compositor = GetCompositor(); - if (compositor) + if (compositor) { compositor->SetLatencyInfo(latency_info); + AddOnCommitCallbackAndDisableLocks( + base::Bind(&RenderWidgetHostViewAura::SendSoftwareFrameAck, + AsWeakPtr(), + output_surface_id)); + } if (paint_observer_) paint_observer_->OnUpdateCompositorContent(); DidReceiveFrameFromRenderer(); + FrameMemoryManager::GetInstance()->AddFrame(this, !host_->is_hidden()); } -void RenderWidgetHostViewAura::SendSoftwareFrameAck( - uint32 output_surface_id, unsigned software_frame_id) { +void RenderWidgetHostViewAura::SendSoftwareFrameAck(uint32 output_surface_id) { + unsigned software_frame_id = 0; + if (!released_software_frames_.empty()) { + unsigned released_output_surface_id = + released_software_frames_.back().output_surface_id; + if (released_output_surface_id == output_surface_id) { + software_frame_id = released_software_frames_.back().frame_id; + released_software_frames_.pop_back(); + } + } + cc::CompositorFrameAck ack; ack.last_software_frame_id = software_frame_id; RenderWidgetHostImpl::SendSwapCompositorFrameAck( host_->GetRoutingID(), output_surface_id, host_->GetProcess()->GetID(), ack); + SendReclaimSoftwareFrames(); +} + +void RenderWidgetHostViewAura::SendReclaimSoftwareFrames() { + while (!released_software_frames_.empty()) { + cc::CompositorFrameAck ack; + ack.last_software_frame_id = released_software_frames_.back().frame_id; + RenderWidgetHostImpl::SendReclaimCompositorResources( + host_->GetRoutingID(), + released_software_frames_.back().output_surface_id, + host_->GetProcess()->GetID(), + ack); + released_software_frames_.pop_back(); + } +} + +void RenderWidgetHostViewAura::ReleaseSoftwareFrame( + uint32 output_surface_id, + unsigned software_frame_id) { + SendReclaimSoftwareFrames(); + released_software_frames_.push_back( + ReleasedFrameInfo(output_surface_id, software_frame_id)); } void RenderWidgetHostViewAura::OnSwapCompositorFrame( uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) { + TRACE_EVENT0("content", "RenderWidgetHostViewAura::OnSwapCompositorFrame"); if (frame->delegated_frame_data) { SwapDelegatedFrame(output_surface_id, frame->delegated_frame_data.Pass(), @@ -1620,6 +1687,8 @@ void RenderWidgetHostViewAura::BuffersSwapped( const BufferPresentedCallback& ack_callback) { scoped_refptr<ui::Texture> previous_texture(current_surface_); const gfx::Rect surface_rect = gfx::Rect(surface_size); + framebuffer_holder_ = NULL; + FrameMemoryManager::GetInstance()->RemoveFrame(this); if (!SwapBuffersPrepare(surface_rect, surface_scale_factor, @@ -1724,7 +1793,7 @@ void RenderWidgetHostViewAura::AcceleratedSurfaceRelease() { } bool RenderWidgetHostViewAura::HasAcceleratedSurface( - const gfx::Size& desired_size) { + const gfx::Size& desired_size) { // Aura doesn't use GetBackingStore for accelerated pages, so it doesn't // matter what is returned here as GetBackingStore is the only caller of this // method. TODO(jbates) implement this if other Aura code needs it. @@ -1756,12 +1825,12 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResult( static void CopyFromCompositingSurfaceFinished( const base::Callback<void(bool, const SkBitmap&)>& callback, - const cc::TextureMailbox::ReleaseCallback& release_callback, + scoped_ptr<cc::SingleReleaseCallback> release_callback, scoped_ptr<SkBitmap> bitmap, scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock, bool result) { bitmap_pixels_lock.reset(); - release_callback.Run(0, false); + release_callback->Run(0, false); callback.Run(result, *bitmap); } @@ -1793,23 +1862,25 @@ void RenderWidgetHostViewAura::PrepareTextureCopyOutputResult( new SkAutoLockPixels(*bitmap)); uint8* pixels = static_cast<uint8*>(bitmap->getPixels()); - scoped_ptr<cc::TextureMailbox> texture_mailbox = result->TakeTexture(); - DCHECK(texture_mailbox->IsTexture()); - if (!texture_mailbox->IsTexture()) + cc::TextureMailbox texture_mailbox; + scoped_ptr<cc::SingleReleaseCallback> release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox->name(), - texture_mailbox->sync_point(), + texture_mailbox.name(), + texture_mailbox.sync_point(), result->size(), gfx::Rect(result->size()), dst_size_in_pixel, pixels, base::Bind(&CopyFromCompositingSurfaceFinished, callback, - texture_mailbox->callback(), + base::Passed(&release_callback), base::Passed(&bitmap), base::Passed(&bitmap_pixels_lock))); } @@ -1831,7 +1902,7 @@ void RenderWidgetHostViewAura::PrepareBitmapCopyOutputResult( if (!source) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); SkBitmap bitmap = skia::ImageOperations::Resize( *source, @@ -1843,9 +1914,9 @@ void RenderWidgetHostViewAura::PrepareBitmapCopyOutputResult( static void CopyFromCompositingSurfaceFinishedForVideo( const base::Callback<void(bool)>& callback, - const cc::TextureMailbox::ReleaseCallback& release_callback, + scoped_ptr<cc::SingleReleaseCallback> release_callback, bool result) { - release_callback.Run(0, false); + release_callback->Run(0, false); callback.Run(result); } @@ -1908,7 +1979,7 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResultForVideo( region_in_frame, video_frame.get()); } - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); callback.Run(true); return; } @@ -1918,9 +1989,11 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResultForVideo( if (!gl_helper) return; - scoped_ptr<cc::TextureMailbox> texture_mailbox = result->TakeTexture(); - DCHECK(texture_mailbox->IsTexture()); - if (!texture_mailbox->IsTexture()) + cc::TextureMailbox texture_mailbox; + scoped_ptr<cc::SingleReleaseCallback> release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) return; gfx::Rect result_rect(result->size()); @@ -1958,14 +2031,14 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceHasResultForVideo( yuv_readback_pipeline = rwhva->yuv_readback_pipeline_.get(); } - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); base::Callback<void(bool result)> finished_callback = base::Bind( &CopyFromCompositingSurfaceFinishedForVideo, callback, - texture_mailbox->callback()); + base::Passed(&release_callback)); yuv_readback_pipeline->ReadbackYUV( - texture_mailbox->name(), - texture_mailbox->sync_point(), + texture_mailbox.name(), + texture_mailbox.sync_point(), video_frame.get(), finished_callback); } @@ -1975,7 +2048,32 @@ void RenderWidgetHostViewAura::GetScreenInfo(WebScreenInfo* results) { } gfx::Rect RenderWidgetHostViewAura::GetBoundsInRootWindow() { +#if defined(OS_WIN) + // aura::Window::GetBoundsInScreen doesn't take non-client area into + // account. + RECT window_rect = {0}; + + aura::Window* top_level = window_->GetToplevelWindow(); + aura::RootWindow* root_window = top_level->GetRootWindow(); + if (!root_window) + return top_level->GetBoundsInScreen(); + HWND hwnd = root_window->GetAcceleratedWidget(); + ::GetWindowRect(hwnd, &window_rect); + gfx::Rect rect(window_rect); + + // Maximized windows are outdented from the work area by the frame thickness + // even though this "frame" is not painted. This confuses code (and people) + // that think of a maximized window as corresponding exactly to the work area. + // Correct for this by subtracting the frame thickness back off. + if (::IsZoomed(hwnd)) { + rect.Inset(GetSystemMetrics(SM_CXSIZEFRAME), + GetSystemMetrics(SM_CYSIZEFRAME)); + } + + return gfx::win::ScreenToDIPRect(rect); +#else return window_->GetToplevelWindow()->GetBoundsInScreen(); +#endif } void RenderWidgetHostViewAura::GestureEventAck(int gesture_event_type, @@ -2004,7 +2102,7 @@ void RenderWidgetHostViewAura::ProcessAckedTouchEvent( } } -SmoothScrollGesture* RenderWidgetHostViewAura::CreateSmoothScrollGesture( +SyntheticGesture* RenderWidgetHostViewAura::CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, @@ -2026,12 +2124,12 @@ void RenderWidgetHostViewAura::SetScrollOffsetPinning( // Not needed. Mac-only. } -void RenderWidgetHostViewAura::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewAura::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { BrowserAccessibilityManager* manager = GetOrCreateBrowserAccessibilityManager(); if (manager) - manager->OnAccessibilityNotifications(params); + manager->OnAccessibilityEvents(params); } gfx::GLSurfaceHandle RenderWidgetHostViewAura::GetCompositingSurface() { @@ -2122,7 +2220,7 @@ void RenderWidgetHostViewAura::SetCompositionText( void RenderWidgetHostViewAura::ConfirmCompositionText() { if (host_ && has_composition_text_) - host_->ImeConfirmComposition(string16(), ui::Range::InvalidRange(), false); + host_->ImeConfirmComposition(string16(), gfx::Range::InvalidRange(), false); has_composition_text_ = false; } @@ -2135,7 +2233,7 @@ void RenderWidgetHostViewAura::ClearCompositionText() { void RenderWidgetHostViewAura::InsertText(const string16& text) { DCHECK(text_input_type_ != ui::TEXT_INPUT_TYPE_NONE); if (host_) - host_->ImeConfirmComposition(text, ui::Range::InvalidRange(), false); + host_->ImeConfirmComposition(text, gfx::Range::InvalidRange(), false); has_composition_text_ = false; } @@ -2166,7 +2264,7 @@ ui::TextInputType RenderWidgetHostViewAura::GetTextInputType() const { } ui::TextInputMode RenderWidgetHostViewAura::GetTextInputMode() const { - return ui::TEXT_INPUT_MODE_DEFAULT; + return text_input_mode_; } bool RenderWidgetHostViewAura::CanComposeInline() const { @@ -2178,18 +2276,18 @@ gfx::Rect RenderWidgetHostViewAura::ConvertRectToScreen(const gfx::Rect& rect) { gfx::Point end = gfx::Point(rect.right(), rect.bottom()); aura::RootWindow* root_window = window_->GetRootWindow(); - if (root_window) { - aura::client::ScreenPositionClient* screen_position_client = - aura::client::GetScreenPositionClient(root_window); - screen_position_client->ConvertPointToScreen(window_, &origin); - screen_position_client->ConvertPointToScreen(window_, &end); - return gfx::Rect(origin.x(), - origin.y(), - end.x() - origin.x(), - end.y() - origin.y()); - } - - return rect; + if (!root_window) + return rect; + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(root_window); + if (!screen_position_client) + return rect; + screen_position_client->ConvertPointToScreen(window_, &origin); + screen_position_client->ConvertPointToScreen(window_, &end); + return gfx::Rect(origin.x(), + origin.y(), + end.x() - origin.x(), + end.y() - origin.y()); } gfx::Rect RenderWidgetHostViewAura::ConvertRectFromScreen( @@ -2231,40 +2329,40 @@ bool RenderWidgetHostViewAura::HasCompositionText() { return has_composition_text_; } -bool RenderWidgetHostViewAura::GetTextRange(ui::Range* range) { +bool RenderWidgetHostViewAura::GetTextRange(gfx::Range* range) { range->set_start(selection_text_offset_); range->set_end(selection_text_offset_ + selection_text_.length()); return true; } -bool RenderWidgetHostViewAura::GetCompositionTextRange(ui::Range* range) { +bool RenderWidgetHostViewAura::GetCompositionTextRange(gfx::Range* range) { // TODO(suzhe): implement this method when fixing http://crbug.com/55130. NOTIMPLEMENTED(); return false; } -bool RenderWidgetHostViewAura::GetSelectionRange(ui::Range* range) { +bool RenderWidgetHostViewAura::GetSelectionRange(gfx::Range* range) { range->set_start(selection_range_.start()); range->set_end(selection_range_.end()); return true; } -bool RenderWidgetHostViewAura::SetSelectionRange(const ui::Range& range) { +bool RenderWidgetHostViewAura::SetSelectionRange(const gfx::Range& range) { // TODO(suzhe): implement this method when fixing http://crbug.com/55130. NOTIMPLEMENTED(); return false; } -bool RenderWidgetHostViewAura::DeleteRange(const ui::Range& range) { +bool RenderWidgetHostViewAura::DeleteRange(const gfx::Range& range) { // TODO(suzhe): implement this method when fixing http://crbug.com/55130. NOTIMPLEMENTED(); return false; } bool RenderWidgetHostViewAura::GetTextFromRange( - const ui::Range& range, + const gfx::Range& range, string16* text) { - ui::Range selection_text_range(selection_text_offset_, + gfx::Range selection_text_range(selection_text_offset_, selection_text_offset_ + selection_text_.length()); if (!selection_text_range.Contains(range)) { @@ -2446,6 +2544,13 @@ void RenderWidgetHostViewAura::OnWindowDestroying() { LPARAM lparam = reinterpret_cast<LPARAM>(this); EnumChildWindows(parent, WindowDestroyingCallback, lparam); #endif + + // Make sure that the input method no longer references to this object before + // this object is removed from the root window (i.e. this object loses access + // to the input method). + ui::InputMethod* input_method = GetInputMethod(); + if (input_method) + input_method->DetachTextInputClient(this); } void RenderWidgetHostViewAura::OnWindowDestroyed() { @@ -2463,28 +2568,53 @@ bool RenderWidgetHostViewAura::HasHitTestMask() const { void RenderWidgetHostViewAura::GetHitTestMask(gfx::Path* mask) const { } -scoped_refptr<ui::Texture> RenderWidgetHostViewAura::CopyTexture() { - if (!host_->is_accelerated_compositing_active()) - return scoped_refptr<ui::Texture>(); - - ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); - GLHelper* gl_helper = factory->GetGLHelper(); - if (!gl_helper) - return scoped_refptr<ui::Texture>(); - - if (!current_surface_.get()) - return scoped_refptr<ui::Texture>(); - - WebKit::WebGLId texture_id = - gl_helper->CopyTexture(current_surface_->PrepareTexture(), - current_surface_->size()); - if (!texture_id) - return scoped_refptr<ui::Texture>(); - - return scoped_refptr<ui::Texture>( - factory->CreateOwnedTexture( +void RenderWidgetHostViewAura::DidRecreateLayer(ui::Layer *old_layer, + ui::Layer *new_layer) { + float mailbox_scale_factor; + cc::TextureMailbox old_mailbox = + old_layer->GetTextureMailbox(&mailbox_scale_factor); + scoped_refptr<ui::Texture> old_texture = old_layer->external_texture(); + // The new_layer is the one that will be used by our Window, so that's the one + // that should keep our texture. old_layer will be returned to the + // RecreateLayer caller, and should have a copy. + if (old_texture.get()) { + ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); + GLHelper* gl_helper = factory->GetGLHelper(); + scoped_refptr<ui::Texture> new_texture; + if (host_->is_accelerated_compositing_active() && + gl_helper && current_surface_.get()) { + WebKit::WebGLId texture_id = + gl_helper->CopyTexture(current_surface_->PrepareTexture(), + current_surface_->size()); + if (texture_id) { + new_texture = factory->CreateOwnedTexture( current_surface_->size(), - current_surface_->device_scale_factor(), texture_id)); + current_surface_->device_scale_factor(), texture_id); + } + } + old_layer->SetExternalTexture(new_texture); + new_layer->SetExternalTexture(old_texture); + } else if (old_mailbox.IsSharedMemory()) { + base::SharedMemory* old_buffer = old_mailbox.shared_memory(); + const size_t size = old_mailbox.shared_memory_size_in_bytes(); + + scoped_ptr<base::SharedMemory> new_buffer(new base::SharedMemory); + new_buffer->CreateAndMapAnonymous(size); + + if (old_buffer->memory() && new_buffer->memory()) { + memcpy(new_buffer->memory(), old_buffer->memory(), size); + base::SharedMemory* new_buffer_raw_ptr = new_buffer.get(); + scoped_ptr<cc::SingleReleaseCallback> callback = + cc::SingleReleaseCallback::Create(base::Bind(MailboxReleaseCallback, + Passed(&new_buffer))); + cc::TextureMailbox new_mailbox(new_buffer_raw_ptr, + old_mailbox.shared_memory_size()); + new_layer->SetTextureMailbox(new_mailbox, + callback.Pass(), + mailbox_scale_factor); + } + } + // TODO(piman): handle delegated frames. } //////////////////////////////////////////////////////////////////////////////// @@ -2859,6 +2989,18 @@ void RenderWidgetHostViewAura::OnRootWindowHostMoved( UpdateScreenInfo(window_); } +void RenderWidgetHostViewAura::ReleaseCurrentFrame() { + if (framebuffer_holder_.get() && !current_surface_.get()) { + framebuffer_holder_ = NULL; + ui::Compositor* compositor = GetCompositor(); + if (compositor) { + AddOnCommitCallbackAndDisableLocks(base::Bind( + &RenderWidgetHostViewAura::SendReclaimSoftwareFrames, AsWeakPtr())); + } + UpdateExternalTexture(); + } +} + //////////////////////////////////////////////////////////////////////////////// // RenderWidgetHostViewAura, ui::CompositorObserver implementation: @@ -2908,8 +3050,17 @@ void RenderWidgetHostViewAura::OnUpdateVSyncParameters( ui::Compositor* compositor, base::TimeTicks timebase, base::TimeDelta interval) { - if (IsShowing() && !last_draw_ended_.is_null()) - host_->UpdateVSyncParameters(last_draw_ended_, interval); + if (IsShowing()) { + if (IsDeadlineSchedulingEnabled()) { + // The deadline scheduler has logic to stagger the draws of the + // Renderer and Browser built-in, so send it an accurate timebase. + host_->UpdateVSyncParameters(timebase, interval); + } else if (!last_draw_ended_.is_null()) { + // For the non-deadline scheduler, we send the Renderer an offset + // vsync timebase to avoid its draws racing the Browser's draws. + host_->UpdateVSyncParameters(last_draw_ended_, interval); + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -3021,6 +3172,7 @@ RenderWidgetHostViewAura::~RenderWidgetHostViewAura() { // Aura root window and we don't have a way to get an input method object // associated with the window, but just in case. DetachFromInputMethod(); + FrameMemoryManager::GetInstance()->RemoveFrame(this); } void RenderWidgetHostViewAura::UpdateCursorIfOverSelf() { @@ -3034,8 +3186,10 @@ void RenderWidgetHostViewAura::UpdateCursorIfOverSelf() { gfx::Point local_point = screen_point; local_point.Offset(-screen_rect.x(), -screen_rect.y()); - if (root_window->GetEventHandlerForPoint(local_point) != window_) + if (!root_window->HasFocus() || + root_window->GetEventHandlerForPoint(local_point) != window_) { return; + } gfx::NativeCursor cursor = current_cursor_.GetNativeCursor(); // Do not show loading cursor when the cursor is currently hidden. @@ -3064,7 +3218,7 @@ void RenderWidgetHostViewAura::FinishImeCompositionSession() { if (!has_composition_text_) return; if (host_) - host_->ImeConfirmComposition(string16(), ui::Range::InvalidRange(), false); + host_->ImeConfirmComposition(string16(), gfx::Range::InvalidRange(), false); ImeCancelComposition(); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura.h b/chromium/content/browser/renderer_host/render_widget_host_view_aura.h index c4a16271634..f5939df0fc7 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura.h @@ -18,6 +18,7 @@ #include "cc/resources/texture_mailbox.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/aura/image_transport_factory.h" +#include "content/browser/renderer_host/frame_memory_manager.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/content_export.h" #include "content/common/gpu/client/gl_helper.h" @@ -59,9 +60,10 @@ namespace content { class MemoryHolder; class RenderWidgetHostImpl; class RenderWidgetHostView; +class ResizeLock; // RenderWidgetHostView class hierarchy described in render_widget_host_view.h. -class RenderWidgetHostViewAura +class CONTENT_EXPORT RenderWidgetHostViewAura : public RenderWidgetHostViewBase, public ui::CompositorObserver, public ui::TextInputClient, @@ -74,6 +76,7 @@ class RenderWidgetHostViewAura public aura::client::CursorClientObserver, public ImageTransportFactoryObserver, public BrowserAccessibilityDelegate, + public FrameContainer, public base::SupportsWeakPtr<RenderWidgetHostViewAura> { public: // Used to notify whenever the paint-content of the view changes. @@ -155,10 +158,6 @@ class RenderWidgetHostViewAura virtual bool IsShowing() OVERRIDE; virtual gfx::Rect GetViewBounds() const OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; -#if defined(OS_WIN) - virtual gfx::NativeViewAccessible AccessibleObjectFromChildId(long child_id) - OVERRIDE; -#endif // Overridden from RenderWidgetHostViewPort: virtual void InitAsPopup(RenderWidgetHostView* parent_host_view, @@ -175,11 +174,11 @@ class RenderWidgetHostViewAura virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -192,7 +191,7 @@ class RenderWidgetHostViewAura virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -227,7 +226,7 @@ class RenderWidgetHostViewAura virtual void ProcessAckedTouchEvent( const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) OVERRIDE; - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, @@ -237,8 +236,8 @@ class RenderWidgetHostViewAura virtual void SetScrollOffsetPinning( bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE; virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; @@ -265,12 +264,12 @@ class RenderWidgetHostViewAura virtual bool GetCompositionCharacterBounds(uint32 index, gfx::Rect* rect) OVERRIDE; virtual bool HasCompositionText() OVERRIDE; - virtual bool GetTextRange(ui::Range* range) OVERRIDE; - virtual bool GetCompositionTextRange(ui::Range* range) OVERRIDE; - virtual bool GetSelectionRange(ui::Range* range) OVERRIDE; - virtual bool SetSelectionRange(const ui::Range& range) OVERRIDE; - virtual bool DeleteRange(const ui::Range& range) OVERRIDE; - virtual bool GetTextFromRange(const ui::Range& range, + virtual bool GetTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetCompositionTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetSelectionRange(gfx::Range* range) OVERRIDE; + virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; + virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; + virtual bool GetTextFromRange(const gfx::Range& range, string16* text) OVERRIDE; virtual void OnInputMethodChanged() OVERRIDE; virtual bool ChangeTextDirectionAndLayoutAlignment( @@ -302,7 +301,8 @@ class RenderWidgetHostViewAura virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; virtual bool HasHitTestMask() const OVERRIDE; virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; - virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE; + virtual void DidRecreateLayer(ui::Layer *old_layer, + ui::Layer *new_layer) OVERRIDE; // Overridden from ui::EventHandler: virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; @@ -329,6 +329,9 @@ class RenderWidgetHostViewAura virtual void OnRootWindowHostMoved(const aura::RootWindow* root, const gfx::Point& new_origin) OVERRIDE; + // FrameContainer implementation: + virtual void ReleaseCurrentFrame() OVERRIDE; + bool CanCopyToBitmap() const; #if defined(OS_WIN) @@ -340,25 +343,21 @@ class RenderWidgetHostViewAura protected: friend class RenderWidgetHostView; + virtual ~RenderWidgetHostViewAura(); - // Should construct only via RenderWidgetHostView::CreateViewForWidget. + // Should be constructed via RenderWidgetHostView::CreateViewForWidget. explicit RenderWidgetHostViewAura(RenderWidgetHost* host); RenderWidgetHostViewFrameSubscriber* frame_subscriber() const { return frame_subscriber_.get(); } - private: - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SetCompositionText); - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventState); - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventSyncAsync); + virtual bool ShouldCreateResizeLock(); + virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock); - class WindowObserver; - friend class WindowObserver; -#if defined(OS_WIN) - class TransientWindowObserver; - friend class TransientWindowObserver; -#endif + // Exposed for tests. + aura::Window* window() { return window_; } + gfx::Size current_frame_size() const { return current_frame_size_; } // Overridden from ui::CompositorObserver: virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE; @@ -372,6 +371,21 @@ class RenderWidgetHostViewAura base::TimeTicks timebase, base::TimeDelta interval) OVERRIDE; + private: + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SetCompositionText); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventState); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, TouchEventSyncAsync); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, SwapNotifiesWindow); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, + SkippedDelegatedFrames); + + class WindowObserver; + friend class WindowObserver; +#if defined(OS_WIN) + class TransientWindowObserver; + friend class TransientWindowObserver; +#endif + // Overridden from ImageTransportFactoryObserver: virtual void OnLostResources() OVERRIDE; @@ -387,8 +401,6 @@ class RenderWidgetHostViewAura virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE; virtual void FatalAccessibilityTreeError() OVERRIDE; - virtual ~RenderWidgetHostViewAura(); - void UpdateCursorIfOverSelf(); bool ShouldSkipFrame(gfx::Size size_in_dip) const; @@ -509,7 +521,9 @@ class RenderWidgetHostViewAura scoped_ptr<cc::SoftwareFrameData> frame_data, float frame_device_scale_factor, const ui::LatencyInfo& latency_info); - void SendSoftwareFrameAck(uint32 output_surface_id, + void SendSoftwareFrameAck(uint32 output_surface_id); + void SendReclaimSoftwareFrames(); + void ReleaseSoftwareFrame(uint32 output_surface_id, unsigned software_frame_id); void DidReceiveFrameFromRenderer(); @@ -565,6 +579,8 @@ class RenderWidgetHostViewAura // The current text input type. ui::TextInputType text_input_type_; + // The current text input mode corresponding to HTML5 inputmode attribute. + ui::TextInputMode text_input_mode_; bool can_compose_inline_; // Rectangles for the selection anchor and focus. @@ -588,12 +604,21 @@ class RenderWidgetHostViewAura // This holds the current software framebuffer. scoped_refptr<MemoryHolder> framebuffer_holder_; + // With delegated renderer, this is the last output surface, used to + // disambiguate resources with the same id coming from different output + // surfaces. + uint32 last_output_surface_id_; + // The damage in the previously presented buffer. SkRegion previous_damage_; // Pending damage from previous frames that we skipped. SkRegion skipped_damage_; + // True after a delegated frame has been skipped, until a frame is not + // skipped. + bool skipped_frames_; + // The size of the last frame that was swapped (even if we skipped it). // Used to determine when the skipped_damage_ needs to be reset due to // size changes between front- and backbuffer. @@ -625,9 +650,6 @@ class RenderWidgetHostViewAura // software backing store is updated. bool accelerated_compositing_state_changed_; - // Used to prevent further resizes while a resize is pending. - class ResizeLock; - // This lock is the one waiting for a frame of the right size to come back // from the renderer/GPU process. It is set from the moment the aura window // got resized, to the moment we committed the renderer frame of the same @@ -700,6 +722,14 @@ class RenderWidgetHostViewAura ui::LatencyInfo software_latency_info_; + struct ReleasedFrameInfo { + ReleasedFrameInfo(uint32 output_id, unsigned software_frame_id) + : output_surface_id(output_id), frame_id(software_frame_id) {} + uint32 output_surface_id; + unsigned frame_id; + }; + std::vector<ReleasedFrameInfo> released_software_frames_; + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura); }; diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc index 03f7d5c591f..824db746ceb 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc @@ -5,16 +5,24 @@ #include "content/browser/renderer_host/render_widget_host_view_aura.h" #include "base/basictypes.h" +#include "base/memory/shared_memory.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "cc/output/compositor_frame.h" +#include "cc/output/compositor_frame_metadata.h" +#include "cc/output/gl_frame_data.h" +#include "content/browser/aura/resize_lock.h" +#include "content/browser/browser_thread_impl.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" #include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/common/gpu/gpu_messages.h" #include "content/common/input_messages.h" #include "content/common/view_messages.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "ipc/ipc_test_sink.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" @@ -26,9 +34,11 @@ #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" #include "ui/aura/window_observer.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" #include "ui/base/ui_base_types.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" + +using testing::_; namespace content { namespace { @@ -69,11 +79,48 @@ class TestWindowObserver : public aura::WindowObserver { DISALLOW_COPY_AND_ASSIGN(TestWindowObserver); }; +class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura { + public: + FakeRenderWidgetHostViewAura(RenderWidgetHost* widget) + : RenderWidgetHostViewAura(widget), has_resize_lock_(false) {} + + virtual ~FakeRenderWidgetHostViewAura() {} + + virtual bool ShouldCreateResizeLock() OVERRIDE { + gfx::Size desired_size = window()->bounds().size(); + return desired_size != current_frame_size(); + } + + virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock) + OVERRIDE { + gfx::Size desired_size = window()->bounds().size(); + return scoped_ptr<ResizeLock>( + new FakeResizeLock(desired_size, defer_compositor_lock)); + } + + void RunOnCompositingDidCommit() { + OnCompositingDidCommit(window()->GetRootWindow()->compositor()); + } + + // A lock that doesn't actually do anything to the compositor, and does not + // time out. + class FakeResizeLock : public ResizeLock { + public: + FakeResizeLock(const gfx::Size new_size, bool defer_compositor_lock) + : ResizeLock(new_size, defer_compositor_lock) {} + }; + + bool has_resize_lock_; + gfx::Size last_frame_size_; +}; + class RenderWidgetHostViewAuraTest : public testing::Test { public: - RenderWidgetHostViewAuraTest() {} + RenderWidgetHostViewAuraTest() + : browser_thread_for_ui_(BrowserThread::UI, &message_loop_) {} virtual void SetUp() { + ImageTransportFactory::InitializeForUnitTests(); aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_)); aura_test_helper_->SetUp(); @@ -84,7 +131,7 @@ class RenderWidgetHostViewAuraTest : public testing::Test { sink_ = &process_host->sink(); parent_host_ = new RenderWidgetHostImpl( - &delegate_, process_host, MSG_ROUTING_NONE); + &delegate_, process_host, MSG_ROUTING_NONE, false); parent_view_ = static_cast<RenderWidgetHostViewAura*>( RenderWidgetHostView::CreateViewForWidget(parent_host_)); parent_view_->InitAsChild(NULL); @@ -92,10 +139,11 @@ class RenderWidgetHostViewAuraTest : public testing::Test { aura_test_helper_->root_window(), gfx::Rect()); widget_host_ = new RenderWidgetHostImpl( - &delegate_, process_host, MSG_ROUTING_NONE); + &delegate_, process_host, MSG_ROUTING_NONE, false); widget_host_->Init(); - view_ = static_cast<RenderWidgetHostViewAura*>( - RenderWidgetHostView::CreateViewForWidget(widget_host_)); + widget_host_->OnMessageReceived( + ViewHostMsg_DidActivateAcceleratedCompositing(0, true)); + view_ = new FakeRenderWidgetHostViewAura(widget_host_); } virtual void TearDown() { @@ -112,10 +160,12 @@ class RenderWidgetHostViewAuraTest : public testing::Test { message_loop_.DeleteSoon(FROM_HERE, browser_context_.release()); message_loop_.RunUntilIdle(); + ImageTransportFactory::Terminate(); } protected: base::MessageLoopForUI message_loop_; + BrowserThreadImpl browser_thread_for_ui_; scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_; scoped_ptr<BrowserContext> browser_context_; MockRenderWidgetHostDelegate delegate_; @@ -128,7 +178,7 @@ class RenderWidgetHostViewAuraTest : public testing::Test { // Tests should set these to NULL if they've already triggered their // destruction. RenderWidgetHostImpl* widget_host_; - RenderWidgetHostViewAura* view_; + FakeRenderWidgetHostViewAura* view_; IPC::TestSink* sink_; @@ -170,6 +220,11 @@ class FullscreenLayoutManager : public aura::LayoutManager { DISALLOW_COPY_AND_ASSIGN(FullscreenLayoutManager); }; +class MockWindowObserver : public aura::WindowObserver { + public: + MOCK_METHOD2(OnWindowPaintScheduled, void(aura::Window*, const gfx::Rect&)); +}; + } // namespace // Checks that a fullscreen view has the correct show-state and receives the @@ -231,7 +286,7 @@ TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) { const ui::CompositionUnderlines& underlines = composition_text.underlines; // Caret is at the end. (This emulates Japanese MSIME 2007 and later) - composition_text.selection = ui::Range(4); + composition_text.selection = gfx::Range(4); sink_->ClearMessages(); view_->SetCompositionText(composition_text); @@ -584,4 +639,209 @@ TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) { } } +scoped_ptr<cc::CompositorFrame> MakeGLFrame(float scale_factor, + gfx::Size size, + gfx::Rect damage) { + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + frame->metadata.device_scale_factor = scale_factor; + frame->gl_frame_data.reset(new cc::GLFrameData); + frame->gl_frame_data->sync_point = 1; + memset(frame->gl_frame_data->mailbox.name, '1', 64); + frame->gl_frame_data->size = size; + frame->gl_frame_data->sub_buffer_rect = damage; + return frame.Pass(); +} + +scoped_ptr<cc::CompositorFrame> MakeSoftwareFrame(float scale_factor, + gfx::Size size, + gfx::Rect damage) { + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + frame->metadata.device_scale_factor = scale_factor; + frame->software_frame_data.reset(new cc::SoftwareFrameData); + frame->software_frame_data->id = 1; + frame->software_frame_data->size = size; + frame->software_frame_data->damage_rect = damage; + base::SharedMemory shm; + shm.CreateAndMapAnonymous(size.GetArea() * 4); + shm.GiveToProcess(base::GetCurrentProcessHandle(), + &frame->software_frame_data->handle); + return frame.Pass(); +} + +scoped_ptr<cc::CompositorFrame> MakeDelegatedFrame(float scale_factor, + gfx::Size size, + gfx::Rect damage) { + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + frame->metadata.device_scale_factor = scale_factor; + frame->delegated_frame_data.reset(new cc::DelegatedFrameData); + + scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create(); + pass->SetNew(cc::RenderPass::Id(1, 1), + gfx::Rect(size), + gfx::RectF(damage), + gfx::Transform()); + frame->delegated_frame_data->render_pass_list.push_back(pass.Pass()); + return frame.Pass(); +} + +// Swapping a frame should notify the window. +TEST_F(RenderWidgetHostViewAuraTest, SwapNotifiesWindow) { + gfx::Size view_size(100, 100); + gfx::Rect view_rect(view_size); + + view_->InitAsChild(NULL); + view_->GetNativeView()->SetDefaultParentByRootWindow( + parent_view_->GetNativeView()->GetRootWindow(), gfx::Rect()); + view_->SetSize(view_size); + view_->WasShown(); + + MockWindowObserver observer; + view_->window_->AddObserver(&observer); + + // Swap a frame through the GPU path. + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; + params.surface_id = widget_host_->surface_id(); + params.route_id = widget_host_->GetRoutingID(); + params.mailbox_name = std::string(64, '1'); + params.size = view_size; + params.scale_factor = 1.f; + + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->AcceleratedSurfaceBuffersSwapped(params, 0); + testing::Mock::VerifyAndClearExpectations(&observer); + + // DSF = 2 + params.size = gfx::Size(200, 200); + params.scale_factor = 2.f; + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->AcceleratedSurfaceBuffersSwapped(params, 0); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Partial frames though GPU path + GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params post_params; + post_params.surface_id = widget_host_->surface_id(); + post_params.route_id = widget_host_->GetRoutingID(); + post_params.mailbox_name = std::string(64, '1'); + post_params.surface_size = gfx::Size(200, 200); + post_params.surface_scale_factor = 2.f; + post_params.x = 40; + post_params.y = 40; + post_params.width = 80; + post_params.height = 80; + // rect from params is upside down, and is inflated in RWHVA, just because. + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(19, 39, 42, 42))); + view_->AcceleratedSurfacePostSubBuffer(post_params, 0); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Composite-to-mailbox path + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame(0, MakeGLFrame(1.f, view_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + + // rect from GL frame is upside down, and is inflated in RWHVA, just because. + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(4, 89, 7, 7))); + view_->OnSwapCompositorFrame( + 0, MakeGLFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Software path + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame(0, MakeSoftwareFrame(1.f, view_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(5, 5, 5, 5))); + view_->OnSwapCompositorFrame( + 0, MakeSoftwareFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); + testing::Mock::VerifyAndClearExpectations(&observer); + + // Delegated renderer path + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, view_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, + gfx::Rect(5, 5, 5, 5))); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5))); + testing::Mock::VerifyAndClearExpectations(&observer); + + view_->window_->RemoveObserver(&observer); +} + +// Skipped frames should not drop their damage. +TEST_F(RenderWidgetHostViewAuraTest, SkippedDelegatedFrames) { + gfx::Rect view_rect(100, 100); + gfx::Size frame_size = view_rect.size(); + + view_->InitAsChild(NULL); + view_->GetNativeView()->SetDefaultParentByRootWindow( + parent_view_->GetNativeView()->GetRootWindow(), gfx::Rect()); + view_->SetSize(view_rect.size()); + + MockWindowObserver observer; + view_->window_->AddObserver(&observer); + + // A full frame of damage. + EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // A partial damage frame. + gfx::Rect partial_view_rect(30, 30, 20, 20); + EXPECT_CALL(observer, + OnWindowPaintScheduled(view_->window_, partial_view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, partial_view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // Lock the compositor. Now we should drop frames. + view_rect = gfx::Rect(150, 150); + view_->SetSize(view_rect.size()); + view_->MaybeCreateResizeLock(); + + // This frame is dropped. + gfx::Rect dropped_damage_rect_1(10, 20, 30, 40); + EXPECT_CALL(observer, OnWindowPaintScheduled(_, _)).Times(0); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, dropped_damage_rect_1)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + gfx::Rect dropped_damage_rect_2(40, 50, 10, 20); + EXPECT_CALL(observer, OnWindowPaintScheduled(_, _)).Times(0); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, dropped_damage_rect_2)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // Unlock the compositor. This frame should damage everything. + frame_size = view_rect.size(); + + gfx::Rect new_damage_rect(5, 6, 10, 10); + EXPECT_CALL(observer, + OnWindowPaintScheduled(view_->window_, view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, new_damage_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + // A partial damage frame, this should not be dropped. + EXPECT_CALL(observer, + OnWindowPaintScheduled(view_->window_, partial_view_rect)); + view_->OnSwapCompositorFrame( + 0, MakeDelegatedFrame(1.f, frame_size, partial_view_rect)); + testing::Mock::VerifyAndClearExpectations(&observer); + view_->RunOnCompositingDidCommit(); + + + view_->window_->RemoveObserver(&observer); +} + } // namespace content diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_base.cc b/chromium/content/browser/renderer_host/render_widget_host_view_base.cc index d6b1e789e2a..57181031f22 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_base.cc @@ -11,7 +11,7 @@ #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/port/browser/render_widget_host_view_frame_subscriber.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/port/browser/synthetic_gesture.h" #include "third_party/WebKit/public/web/WebScreenInfo.h" #include "ui/gfx/display.h" #include "ui/gfx/screen.h" @@ -29,9 +29,9 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_data.h" #include "content/public/common/content_switches.h" -#include "ui/base/win/dpi.h" -#include "ui/base/win/hwnd_util.h" #include "ui/gfx/gdi_util.h" +#include "ui/gfx/win/dpi.h" +#include "ui/gfx/win/hwnd_util.h" #endif #if defined(TOOLKIT_GTK) @@ -118,7 +118,7 @@ LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, } bool IsPluginWrapperWindow(HWND window) { - return ui::GetClassNameW(window) == + return gfx::GetClassNameW(window) == string16(kWrapperNativeWindowClassName); } @@ -151,7 +151,7 @@ HWND ReparentWindow(HWND window, HWND parent) { MAKEINTATOM(atom), 0, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0, parent, 0, instance, 0); - ui::CheckWindowCreated(new_parent); + gfx::CheckWindowCreated(new_parent); ::SetParent(window, new_parent); // How many times we try to find a PluginProcessHost whose process matches // the HWND. @@ -169,7 +169,7 @@ BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) { return TRUE; gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); - gfx::Rect rect_in_pixels = ui::win::DIPToScreenRect(*rect); + gfx::Rect rect_in_pixels = gfx::win::DIPToScreenRect(*rect); static UINT msg = RegisterWindowMessage(kPaintMessageName); WPARAM wparam = MAKEWPARAM(rect_in_pixels.x(), rect_in_pixels.y()); lparam = MAKELPARAM(rect_in_pixels.width(), rect_in_pixels.height()); @@ -284,7 +284,7 @@ void RenderWidgetHostViewBase::MovePluginWindowsHelper( #endif if (move.rects_valid) { - gfx::Rect clip_rect_in_pixel = ui::win::DIPToScreenRect(move.clip_rect); + gfx::Rect clip_rect_in_pixel = gfx::win::DIPToScreenRect(move.clip_rect); HRGN hrgn = ::CreateRectRgn(clip_rect_in_pixel.x(), clip_rect_in_pixel.y(), clip_rect_in_pixel.right(), @@ -313,7 +313,7 @@ void RenderWidgetHostViewBase::MovePluginWindowsHelper( } gfx::Rect window_rect_in_pixel = - ui::win::DIPToScreenRect(move.window_rect); + gfx::win::DIPToScreenRect(move.window_rect); defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, window, NULL, window_rect_in_pixel.x(), @@ -382,7 +382,7 @@ RenderWidgetHostViewBase::RenderWidgetHostViewBase() mouse_locked_(false), showing_context_menu_(false), selection_text_offset_(0), - selection_range_(ui::Range::InvalidRange()), + selection_range_(gfx::Range::InvalidRange()), current_device_scale_factor_(0), renderer_frame_number_(0) { } @@ -417,7 +417,7 @@ float RenderWidgetHostViewBase::GetOverdrawBottomHeight() const { void RenderWidgetHostViewBase::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { selection_text_ = text; selection_text_offset_ = offset; selection_range_.set_start(range.start()); @@ -501,13 +501,21 @@ bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) { return true; } -SmoothScrollGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture( +SyntheticGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) { return new BasicMouseWheelSmoothScrollGesture(scroll_down, pixels_to_scroll, mouse_event_x, mouse_event_y); } +SyntheticGesture* RenderWidgetHostViewBase::CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) { + // There is no generic implementation for pinch gestures. + NOTIMPLEMENTED(); + return NULL; +} + void RenderWidgetHostViewBase::ProcessAckedTouchEvent( const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) { } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_base.h b/chromium/content/browser/renderer_host/render_widget_host_view_base.h index 68fcaffaea7..0472153b275 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_base.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_base.h @@ -20,8 +20,8 @@ #include "base/callback_forward.h" #include "content/common/content_export.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "ui/base/range/range.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/range/range.h" #include "ui/gfx/rect.h" namespace content { @@ -48,7 +48,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; virtual const SkBitmap& GetBackground() OVERRIDE; virtual gfx::Size GetPhysicalBackingSize() const OVERRIDE; @@ -69,9 +69,12 @@ class CONTENT_EXPORT RenderWidgetHostViewBase GetBrowserAccessibilityManager() const OVERRIDE; virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) OVERRIDE; - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) OVERRIDE; + virtual SyntheticGesture* CreatePinchGesture( + bool zoom_in, int pixels_to_move, int anchor_x, + int anchor_y) OVERRIDE; virtual bool CanSubscribeFrame() const OVERRIDE; virtual void BeginFrameSubscription( scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE; @@ -144,7 +147,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase size_t selection_text_offset_; // The current selection range relative to the start of the web page. - ui::Range selection_range_; + gfx::Range selection_range_; protected: // The scale factor of the display the renderer is currently on. diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc index 2b47ff19315..0fe376e65b3 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_browsertest.cc @@ -6,29 +6,30 @@ #include "base/message_loop/message_loop_proxy.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/renderer_host/dip_util.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/port/browser/render_widget_host_view_frame_subscriber.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "content/public/browser/compositor_util.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "media/base/video_frame.h" #include "media/filters/skcanvas_video_renderer.h" #include "net/base/net_util.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkDevice.h" #include "ui/base/ui_base_switches.h" #include "ui/gfx/size_conversions.h" +#include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" #if defined(OS_MACOSX) @@ -36,7 +37,8 @@ #endif #if defined(OS_WIN) -#include "ui/base/win/dpi.h" +#include "base/win/windows_version.h" +#include "ui/gfx/win/dpi.h" #endif namespace content { @@ -341,6 +343,14 @@ class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber { // is enabled. IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, CopyFromBackingStore) { + // Disable the test for WinXP. See http://crbug/294116. +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + LOG(WARNING) << "Test disabled due to unknown bug on WinXP."; + return; + } +#endif + RunBasicCopyFromBackingStoreTest(); } @@ -373,8 +383,18 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, // Tests that the callback passed to CopyFromCompositingSurfaceToVideoFrame is // always called, even when the RenderWidgetHost is deleting in the middle of // an async copy. +// +// Test is flaky on Win Aura. http://crbug.com/276783 +#if (defined(OS_WIN) && defined(USE_AURA)) || \ + (defined(OS_CHROMEOS) && !defined(NDEBUG)) +#define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \ + DISABLED_CopyFromCompositingSurface_CallbackDespiteDelete +#else +#define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \ + CopyFromCompositingSurface_CallbackDespiteDelete +#endif IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, - CopyFromCompositingSurface_CallbackDespiteDelete) { + MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete) { SET_UP_SURFACE_OR_PASS_TEST(NULL); RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort(); if (!view->CanCopyToVideoFrame()) { @@ -410,6 +430,14 @@ IN_PROC_BROWSER_TEST_F(NonCompositingRenderWidgetHostViewBrowserTest, // until at least one DeliverFrameCallback has been invoked. IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, FrameSubscriberTest) { + // Disable the test for WinXP. See http://crbug/294116. +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + LOG(WARNING) << "Test disabled due to unknown bug on WinXP."; + return; + } +#endif + SET_UP_SURFACE_OR_PASS_TEST(NULL); RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort(); if (!view->CanSubscribeFrame()) { @@ -435,6 +463,14 @@ IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, // Test that we can copy twice from an accelerated composited page. IN_PROC_BROWSER_TEST_F(CompositingRenderWidgetHostViewBrowserTest, CopyTwice) { + // Disable the test for WinXP. See http://crbug/294116. +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + LOG(WARNING) << "Test disabled due to unknown bug on WinXP."; + return; + } +#endif + SET_UP_SURFACE_OR_PASS_TEST(NULL); RenderWidgetHostViewPort* const view = GetRenderWidgetHostViewPort(); if (!view->CanCopyToVideoFrame()) { @@ -558,7 +594,7 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture bitmap.allocPixels(); bitmap.setIsOpaque(true); - SkDevice device(bitmap); + SkBitmapDevice device(bitmap); SkCanvas canvas(&device); video_renderer.Paint(video_frame.get(), @@ -638,7 +674,8 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture // The page is loaded in the renderer, wait for a new frame to arrive. uint32 frame = rwhvp->RendererFrameNumber(); - GetRenderWidgetHost()->ScheduleComposite(); + while (!GetRenderWidgetHost()->ScheduleComposite()) + GiveItSomeTime(); while (rwhvp->RendererFrameNumber() == frame) GiveItSomeTime(); @@ -824,7 +861,7 @@ class CompositingRenderWidgetHostViewTabCaptureHighDPI base::StringPrintf("%f", scale())); #if defined(OS_WIN) cmd->AppendSwitchASCII(switches::kHighDPISupport, "1"); - ui::EnableHighDPISupport(); + gfx::EnableHighDPISupport(); #endif } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc index ddaf9528cfd..61512ef7506 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.cc @@ -29,6 +29,7 @@ #include "content/browser/renderer_host/gtk_im_context_wrapper.h" #include "content/browser/renderer_host/gtk_key_bindings_handler.h" #include "content/browser/renderer_host/gtk_window_utils.h" +#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/common/gpu/gpu_messages.h" @@ -40,17 +41,15 @@ #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebScreenInfo.h" -#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" -#include "ui/base/gtk/gtk_compat.h" -#include "ui/base/text/text_elider.h" #include "ui/base/x/active_window_watcher_x.h" #include "ui/base/x/x11_util.h" +#include "ui/gfx/gtk_compat.h" #include "ui/gfx/gtk_native_view_id_manager.h" #include "ui/gfx/gtk_preserve_window.h" +#include "ui/gfx/text_elider.h" #include "webkit/common/cursors/webcursor_gtk_data.h" -using WebKit::WebInputEventFactory; using WebKit::WebMouseWheelEvent; using WebKit::WebScreenInfo; @@ -70,10 +69,6 @@ namespace { const int kMaxWindowWidth = 10000; const int kMaxWindowHeight = 10000; -// See WebInputEventFactor.cpp for a reason for this being the default -// scroll size for linux. -const float kDefaultScrollPixelsPerTick = 160.0f / 3.0f; - const GdkColor kBGColor = #if defined(NDEBUG) { 0, 0xff * 257, 0xff * 257, 0xff * 257 }; @@ -354,7 +349,7 @@ class RenderWidgetHostViewGtkWidget { RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); if (widget_host) - widget_host->ForwardMouseEvent(WebInputEventFactory::mouseEvent(event)); + widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event)); // Although we did handle the mouse event, we need to let other handlers // run (in particular the one installed by WebContentsViewGtk). @@ -380,8 +375,7 @@ class RenderWidgetHostViewGtkWidget { host_view->ModifyEventForEdgeDragging(widget, event); - WebKit::WebMouseEvent mouse_event = - WebInputEventFactory::mouseEvent(event); + WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); if (host_view->mouse_locked_) { gfx::Point center = host_view->GetWidgetCenter(); @@ -431,8 +425,7 @@ class RenderWidgetHostViewGtkWidget { // additionally send this crossing event with the state indicating the // button is down, it causes problems with drag and drop in WebKit.) if (!(event->state & any_button_mask)) { - WebKit::WebMouseEvent mouse_event = - WebInputEventFactory::mouseEvent(event); + WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event); host_view->ModifyEventMovementAndCoords(&mouse_event); // When crossing out and back into a render view the movement values // must represent the instantaneous movement of the mouse, not the jump @@ -500,7 +493,7 @@ class RenderWidgetHostViewGtkWidget { gdk_event_put(event); gdk_event_free(event); } - return num_clicks * kDefaultScrollPixelsPerTick; + return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick(); } static gboolean OnMouseScrollEvent(GtkWidget* widget, @@ -518,21 +511,23 @@ class RenderWidgetHostViewGtkWidget { event->direction = GDK_SCROLL_RIGHT; } - WebMouseWheelEvent web_event = WebInputEventFactory::mouseWheelEvent(event); + WebMouseWheelEvent web_event = WebMouseWheelEventBuilder::Build(event); + const float pixelsPerTick = + WebMouseWheelEventBuilder::ScrollbarPixelsPerTick(); // We peek ahead at the top of the queue to look for additional pending // scroll events. if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN) { if (event->direction == GDK_SCROLL_UP) - web_event.deltaY = kDefaultScrollPixelsPerTick; + web_event.deltaY = pixelsPerTick; else - web_event.deltaY = -kDefaultScrollPixelsPerTick; + web_event.deltaY = -pixelsPerTick; web_event.deltaY += GetPendingScrollDelta(true, event->state); } else { if (event->direction == GDK_SCROLL_LEFT) - web_event.deltaX = kDefaultScrollPixelsPerTick; + web_event.deltaX = pixelsPerTick; else - web_event.deltaX = -kDefaultScrollPixelsPerTick; + web_event.deltaX = -pixelsPerTick; web_event.deltaX += GetPendingScrollDelta(false, event->state); } RenderWidgetHostImpl::From( @@ -546,7 +541,7 @@ class RenderWidgetHostViewGtkWidget { RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) : host_(RenderWidgetHostImpl::From(widget_host)), about_to_validate_and_paint_(false), - is_hidden_(false), + is_hidden_(host_->is_hidden()), is_loading_(false), parent_(NULL), is_popup_first_mouse_release_(true), @@ -686,17 +681,18 @@ RenderWidgetHost* RenderWidgetHostViewGtk::GetRenderWidgetHost() const { } void RenderWidgetHostViewGtk::WasShown() { - if (!is_hidden_) + if (!host_ || !is_hidden_) return; if (web_contents_switch_paint_time_.is_null()) web_contents_switch_paint_time_ = base::TimeTicks::Now(); is_hidden_ = false; + host_->WasShown(); } void RenderWidgetHostViewGtk::WasHidden() { - if (is_hidden_) + if (!host_ || is_hidden_) return; // If we receive any more paint messages while we are hidden, we want to @@ -795,10 +791,12 @@ bool RenderWidgetHostViewGtk::IsSurfaceAvailableForCopy() const { void RenderWidgetHostViewGtk::Show() { gtk_widget_show(view_.get()); + WasShown(); } void RenderWidgetHostViewGtk::Hide() { gtk_widget_hide(view_.get()); + WasHidden(); } bool RenderWidgetHostViewGtk::IsShowing() { @@ -837,8 +835,8 @@ void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) { void RenderWidgetHostViewGtk::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { im_context_->UpdateInputMethodState(type, can_compose_inline); } @@ -936,7 +934,7 @@ void RenderWidgetHostViewGtk::SetTooltipText(const string16& tooltip_text) { // this itself). // I filed https://bugzilla.gnome.org/show_bug.cgi?id=604641 upstream. const string16 clamped_tooltip = - ui::TruncateString(tooltip_text, kMaxTooltipLength); + gfx::TruncateString(tooltip_text, kMaxTooltipLength); if (clamped_tooltip.empty()) { gtk_widget_set_has_tooltip(view_.get(), FALSE); @@ -948,7 +946,7 @@ void RenderWidgetHostViewGtk::SetTooltipText(const string16& tooltip_text) { void RenderWidgetHostViewGtk::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { RenderWidgetHostViewBase::SelectionChanged(text, offset, range); if (text.empty() || range.is_empty()) @@ -1542,8 +1540,8 @@ void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() { } } -void RenderWidgetHostViewGtk::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewGtk::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (!GetBrowserAccessibilityManager()) { GtkWidget* parent = gtk_widget_get_parent(view_.get()); SetBrowserAccessibilityManager( @@ -1552,7 +1550,7 @@ void RenderWidgetHostViewGtk::OnAccessibilityNotifications( BrowserAccessibilityManagerGtk::GetEmptyDocument(), this)); } - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } AtkObject* RenderWidgetHostViewGtk::GetAccessible() { diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h index 9ff410a07b1..fc57a1a5aad 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_gtk.h @@ -17,8 +17,6 @@ #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/content_export.h" #include "ipc/ipc_sender.h" -#include "ui/base/animation/animation_delegate.h" -#include "ui/base/animation/slide_animation.h" #include "ui/base/gtk/gtk_signal.h" #include "ui/base/gtk/gtk_signal_registrar.h" #include "ui/base/gtk/owned_widget_gtk.h" @@ -83,8 +81,8 @@ class CONTENT_EXPORT RenderWidgetHostViewGtk virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -98,7 +96,7 @@ class CONTENT_EXPORT RenderWidgetHostViewGtk virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -131,8 +129,8 @@ class CONTENT_EXPORT RenderWidgetHostViewGtk virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; // ActiveWindowWatcherXObserver implementation. diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc index b49ca41054e..05730b92545 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_guest.cc @@ -51,7 +51,7 @@ RenderWidgetHostViewGuest::RenderWidgetHostViewGuest( RenderWidgetHostView* platform_view) : host_(RenderWidgetHostImpl::From(widget_host)), guest_(guest), - is_hidden_(false), + is_hidden_(host_->is_hidden()), platform_view_(static_cast<RenderWidgetHostViewPort*>(platform_view)) { #if defined(OS_WIN) || defined(USE_AURA) gesture_recognizer_.reset(ui::GestureRecognizer::Create(this)); @@ -289,11 +289,11 @@ void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading) { void RenderWidgetHostViewGuest::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { RenderWidgetHostViewPort::FromRWHV( guest_->GetEmbedderRenderWidgetHostView())-> - TextInputTypeChanged(type, can_compose_inline, input_mode); + TextInputTypeChanged(type, input_mode, can_compose_inline); } void RenderWidgetHostViewGuest::ImeCancelComposition() { @@ -302,7 +302,7 @@ void RenderWidgetHostViewGuest::ImeCancelComposition() { #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) void RenderWidgetHostViewGuest::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { } #endif @@ -317,7 +317,7 @@ void RenderWidgetHostViewGuest::DidUpdateBackingStore( void RenderWidgetHostViewGuest::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { platform_view_->SelectionChanged(text, offset, range); } @@ -375,14 +375,6 @@ void RenderWidgetHostViewGuest::SetClickthroughRegion(SkRegion* region) { } #endif -#if defined(OS_WIN) && defined(USE_AURA) -gfx::NativeViewAccessible -RenderWidgetHostViewGuest::AccessibleObjectFromChildId(long child_id) { - NOTIMPLEMENTED(); - return NULL; -} -#endif - void RenderWidgetHostViewGuest::SetHasHorizontalScrollbar( bool has_horizontal_scrollbar) { platform_view_->SetHasHorizontalScrollbar(has_horizontal_scrollbar); @@ -412,8 +404,8 @@ void RenderWidgetHostViewGuest::GetScreenInfo(WebKit::WebScreenInfo* results) { embedder_view->GetScreenInfo(results); } -void RenderWidgetHostViewGuest::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewGuest::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { } #if defined(OS_MACOSX) diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest.h b/chromium/content/browser/renderer_host/render_widget_host_view_guest.h index 15b0acc1a8b..82f2eab1bac 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_guest.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_guest.h @@ -10,9 +10,9 @@ #include "base/memory/scoped_ptr.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/content_export.h" -#include "ui/base/events/event.h" #include "ui/base/gestures/gesture_recognizer.h" #include "ui/base/gestures/gesture_types.h" +#include "ui/events/event.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/vector2d_f.h" @@ -65,10 +65,6 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest #if defined(OS_WIN) && !defined(USE_AURA) virtual void SetClickthroughRegion(SkRegion* region) OVERRIDE; #endif -#if defined(OS_WIN) && defined(USE_AURA) - virtual gfx::NativeViewAccessible AccessibleObjectFromChildId(long child_id) - OVERRIDE; -#endif // RenderWidgetHostViewPort implementation. virtual void InitAsPopup(RenderWidgetHostView* parent_host_view, @@ -85,12 +81,12 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; #endif virtual void DidUpdateBackingStore( @@ -105,7 +101,7 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -146,8 +142,8 @@ class CONTENT_EXPORT RenderWidgetHostViewGuest virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; virtual void GetScreenInfo(WebKit::WebScreenInfo* results) OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE; #if defined(OS_MACOSX) diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc index 959f5e404a3..a112634b69d 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_guest_unittest.cc @@ -32,7 +32,7 @@ class RenderWidgetHostViewGuestTest : public testing::Test { MockRenderProcessHost* process_host = new MockRenderProcessHost(browser_context_.get()); widget_host_ = new RenderWidgetHostImpl( - &delegate_, process_host, MSG_ROUTING_NONE); + &delegate_, process_host, MSG_ROUTING_NONE, false); view_ = new RenderWidgetHostViewGuest( widget_host_, NULL, new TestRenderWidgetHostView(widget_host_)); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac.h b/chromium/content/browser/renderer_host/render_widget_host_view_mac.h index 6cbe6dc7b69..11b1d973a42 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac.h @@ -248,11 +248,11 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -265,7 +265,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, virtual void SetTooltipText(const string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) OVERRIDE; + const gfx::Range& range) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; @@ -284,8 +284,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE; virtual void EndFrameSubscription() OVERRIDE; virtual void OnAcceleratedCompositingStateChange() OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params ) OVERRIDE; virtual bool PostProcessEventForPluginIme( const NativeWebKeyboardEvent& event) OVERRIDE; @@ -354,19 +354,19 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, // point to |line_breaking_point|. The |line_break_point| is valid only if // this function returns true. bool GetLineBreakIndex(const std::vector<gfx::Rect>& bounds, - const ui::Range& range, + const gfx::Range& range, size_t* line_break_point); // Returns composition character boundary rectangle. The |range| is // composition based range. Also stores |actual_range| which is corresponding // to actually used range for returned rectangle. - gfx::Rect GetFirstRectForCompositionRange(const ui::Range& range, - ui::Range* actual_range); + gfx::Rect GetFirstRectForCompositionRange(const gfx::Range& range, + gfx::Range* actual_range); // Converts from given whole character range to composition oriented range. If - // the conversion failed, return ui::Range::InvalidRange. - ui::Range ConvertCharacterRangeToCompositionRange( - const ui::Range& request_range); + // the conversion failed, return gfx::Range::InvalidRange. + gfx::Range ConvertCharacterRangeToCompositionRange( + const gfx::Range& request_range); // These member variables should be private, but the associated ObjC class // needs access to them and can't be made a friend. @@ -548,7 +548,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostViewBase, base::Time next_swap_ack_time_; // The current composition character range and its bounds. - ui::Range composition_range_; + gfx::Range composition_range_; std::vector<gfx::Rect> composition_bounds_; // The current caret bounds. diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm index 07448884b5d..b6a6c180bdf 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -7,8 +7,9 @@ #import <objc/runtime.h> #include <QuartzCore/QuartzCore.h> +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/debug/crash_logging.h" #include "base/debug/trace_event.h" @@ -54,7 +55,7 @@ #include "ui/base/cocoa/animation_utils.h" #import "ui/base/cocoa/fullscreen_window_manager.h" #import "ui/base/cocoa/underlay_opengl_hosting_window.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/base/layout.h" #include "ui/gfx/display.h" #include "ui/gfx/point.h" @@ -425,7 +426,7 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) allow_overlapping_views_(false), use_core_animation_(false), is_loading_(false), - is_hidden_(false), + is_hidden_(render_widget_host_->is_hidden()), weak_factory_(this), fullscreen_parent_host_view_(NULL), pending_swap_buffers_acks_weak_factory_(this), @@ -869,6 +870,10 @@ void RenderWidgetHostViewMac::Show() { } void RenderWidgetHostViewMac::Hide() { + // We're messing with the window, so do this to ensure no flashes. + if (!use_core_animation_) + [[cocoa_view_ window] disableScreenUpdatesUntilFlush]; + [cocoa_view_ setHidden:YES]; WasHidden(); @@ -905,8 +910,8 @@ void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) { void RenderWidgetHostViewMac::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (text_input_type_ != type || can_compose_inline_ != can_compose_inline) { text_input_type_ = type; @@ -930,7 +935,7 @@ void RenderWidgetHostViewMac::ImeCancelComposition() { } void RenderWidgetHostViewMac::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { // The RangeChanged message is only sent with valid values. The current // caret position (start == end) will be sent if there is no IME range. @@ -1074,7 +1079,7 @@ void RenderWidgetHostViewMac::StopSpeaking() { // void RenderWidgetHostViewMac::SelectionChanged(const string16& text, size_t offset, - const ui::Range& range) { + const gfx::Range& range) { if (range.is_empty() || text.empty()) { selected_text_.clear(); } else { @@ -1161,7 +1166,7 @@ void RenderWidgetHostViewMac::CopyFromCompositingSurface( gfx::Size dst_pixel_size = gfx::ToFlooredSize( gfx::ScaleSize(dst_size, scale)); - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect), dst_pixel_size, @@ -1192,7 +1197,7 @@ void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame( if (src_subrect.IsEmpty()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); compositing_iosurface_->CopyToVideoFrame( GetScaledOpenGLPixelRect(src_subrect), target, @@ -1485,7 +1490,7 @@ void RenderWidgetHostViewMac::GetVSyncParameters( bool RenderWidgetHostViewMac::GetLineBreakIndex( const std::vector<gfx::Rect>& bounds, - const ui::Range& range, + const gfx::Range& range, size_t* line_break_point) { DCHECK(line_break_point); if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty()) @@ -1515,8 +1520,8 @@ bool RenderWidgetHostViewMac::GetLineBreakIndex( } gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( - const ui::Range& range, - ui::Range* actual_range) { + const gfx::Range& range, + gfx::Range* actual_range) { DCHECK(actual_range); DCHECK(!composition_bounds_.empty()); DCHECK(range.start() <= composition_bounds_.size()); @@ -1541,7 +1546,7 @@ gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) { end_idx = range.end(); } - *actual_range = ui::Range(range.start(), end_idx); + *actual_range = gfx::Range(range.start(), end_idx); gfx::Rect rect = composition_bounds_[range.start()]; for (size_t i = range.start() + 1; i < end_idx; ++i) { rect.Union(composition_bounds_[i]); @@ -1549,21 +1554,21 @@ gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( return rect; } -ui::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange( - const ui::Range& request_range) { +gfx::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange( + const gfx::Range& request_range) { if (composition_range_.is_empty()) - return ui::Range::InvalidRange(); + return gfx::Range::InvalidRange(); if (request_range.is_reversed()) - return ui::Range::InvalidRange(); + return gfx::Range::InvalidRange(); if (request_range.start() < composition_range_.start() || request_range.start() > composition_range_.end() || request_range.end() > composition_range_.end()) { - return ui::Range::InvalidRange(); + return gfx::Range::InvalidRange(); } - return ui::Range( + return gfx::Range( request_range.start() - composition_range_.start(), request_range.end() - composition_range_.start()); } @@ -1578,16 +1583,16 @@ bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange( "RenderWidgetHostViewMac::GetFirstRectForCharacterRange"); // If requested range is same as caret location, we can just return it. - if (selection_range_.is_empty() && ui::Range(range) == selection_range_) { + if (selection_range_.is_empty() && gfx::Range(range) == selection_range_) { if (actual_range) *actual_range = range; *rect = NSRectFromCGRect(caret_rect_.ToCGRect()); return true; } - const ui::Range request_range_in_composition = - ConvertCharacterRangeToCompositionRange(ui::Range(range)); - if (request_range_in_composition == ui::Range::InvalidRange()) + const gfx::Range request_range_in_composition = + ConvertCharacterRangeToCompositionRange(gfx::Range(range)); + if (request_range_in_composition == gfx::Range::InvalidRange()) return false; // If firstRectForCharacterRange in WebFrame is failed in renderer, @@ -1596,12 +1601,12 @@ bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange( return false; DCHECK_EQ(composition_bounds_.size(), composition_range_.length()); - ui::Range ui_actual_range; + gfx::Range ui_actual_range; *rect = NSRectFromCGRect(GetFirstRectForCompositionRange( request_range_in_composition, &ui_actual_range).ToCGRect()); if (actual_range) { - *actual_range = ui::Range( + *actual_range = gfx::Range( composition_range_.start() + ui_actual_range.start(), composition_range_.start() + ui_actual_range.end()).ToNSRange(); } @@ -1787,6 +1792,10 @@ void RenderWidgetHostViewMac::GotSoftwareFrame() { // Also note that it is necessary that clearDrawable be called if // overlapping views are not allowed, e.g, for content shell. // http://crbug.com/178408 + // Disable screen updates so that the changes of flashes is minimized. + // http://crbug.com/279472 + if (!use_core_animation_) + [[cocoa_view_ window] disableScreenUpdatesUntilFlush]; if (allow_overlapping_views_) DestroyCompositedIOSurfaceAndLayer(kLeaveContextBoundToView); else @@ -1853,8 +1862,8 @@ void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { render_widget_host_->GetRoutingID(), background)); } -void RenderWidgetHostViewMac::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewMac::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { if (!GetBrowserAccessibilityManager()) { SetBrowserAccessibilityManager( new BrowserAccessibilityManagerMac( @@ -1862,7 +1871,7 @@ void RenderWidgetHostViewMac::OnAccessibilityNotifications( BrowserAccessibilityManagerMac::GetEmptyDocument(), NULL)); } - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } void RenderWidgetHostViewMac::SetTextInputActive(bool active) { @@ -2325,7 +2334,7 @@ void RenderWidgetHostViewMac::FrameSwapped() { if (textToBeInserted_.length() > ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) { widgetHost->ImeConfirmComposition( - textToBeInserted_, ui::Range::InvalidRange(), false); + textToBeInserted_, gfx::Range::InvalidRange(), false); textInserted = YES; } @@ -2342,7 +2351,7 @@ void RenderWidgetHostViewMac::FrameSwapped() { } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) { if (unmarkTextCalled_) { widgetHost->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); } else { widgetHost->ImeCancelComposition(); } @@ -3422,7 +3431,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName; // called in keyEvent: method. if (!handlingKeyDown_) { renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); } else { unmarkTextCalled_ = YES; } @@ -3515,7 +3524,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName; if (handlingKeyDown_) { textToBeInserted_.append(base::SysNSStringToUTF16(im_text)); } else { - ui::Range replacement_range(replacementRange); + gfx::Range replacement_range(replacementRange); renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( base::SysNSStringToUTF16(im_text), replacement_range, false); } @@ -3660,7 +3669,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName; if (renderWidgetHostView_->render_widget_host_) renderWidgetHostView_->render_widget_host_->ImeConfirmComposition( - string16(), ui::Range::InvalidRange(), false); + string16(), gfx::Range::InvalidRange(), false); [self cancelComposition]; } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm index 0d3ee0a8aa0..ba92843ebf7 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm @@ -78,7 +78,7 @@ class RenderWidgetHostEditCommandCounter : public RenderWidgetHostImpl { RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id), + : RenderWidgetHostImpl(delegate, process, routing_id, false), edit_command_message_count_(0) { } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm index 3c91bc6abdf..7a152d18da8 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm +++ b/chromium/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm @@ -101,7 +101,7 @@ class MockRenderWidgetHostImpl : public RenderWidgetHostImpl { MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id) { + : RenderWidgetHostImpl(delegate, process, routing_id, false) { } MOCK_METHOD0(Focus, void()); @@ -140,7 +140,7 @@ void GenerateCompositionRectArray(const gfx::Point& origin, gfx::Rect GetExpectedRect(const gfx::Point& origin, const gfx::Size& size, - const ui::Range& range, + const gfx::Range& range, int line_no) { return gfx::Rect( origin.x() + range.start() * size.width(), @@ -264,7 +264,7 @@ TEST_F(RenderWidgetHostViewMacTest, FullscreenCloseOnEscape) { new MockRenderProcessHost(&browser_context); // Owned by its |cocoa_view()|. RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl( - &delegate, process_host, MSG_ROUTING_NONE); + &delegate, process_host, MSG_ROUTING_NONE, false); RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>( RenderWidgetHostView::CreateViewForWidget(rwh)); @@ -298,7 +298,7 @@ TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) { new MockRenderProcessHost(&browser_context); // Owned by its |cocoa_view()|. RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl( - &delegate, process_host, MSG_ROUTING_NONE); + &delegate, process_host, MSG_ROUTING_NONE, false); RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>( RenderWidgetHostView::CreateViewForWidget(rwh)); @@ -322,7 +322,7 @@ TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) { const size_t kDummyOffset = 0; gfx::Rect caret_rect(10, 11, 0, 10); - ui::Range caret_range(0, 0); + gfx::Range caret_range(0, 0); ViewHostMsg_SelectionBounds_Params params; NSRect rect; @@ -336,24 +336,24 @@ TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) { &rect, &actual_range)); EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect))); - EXPECT_EQ(caret_range, ui::Range(actual_range)); + EXPECT_EQ(caret_range, gfx::Range(actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 1).ToNSRange(), + gfx::Range(0, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 3).ToNSRange(), + gfx::Range(2, 3).ToNSRange(), &rect, &actual_range)); // Caret moved. caret_rect = gfx::Rect(20, 11, 0, 10); - caret_range = ui::Range(1, 1); + caret_range = gfx::Range(1, 1); params.anchor_rect = params.focus_rect = caret_rect; rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range); rwhv_mac_->SelectionBoundsChanged(params); @@ -362,45 +362,45 @@ TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) { &rect, &actual_range)); EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect))); - EXPECT_EQ(caret_range, ui::Range(actual_range)); + EXPECT_EQ(caret_range, gfx::Range(actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 3).ToNSRange(), + gfx::Range(2, 3).ToNSRange(), &rect, &actual_range)); // No caret. - caret_range = ui::Range(1, 2); + caret_range = gfx::Range(1, 2); rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range); params.anchor_rect = caret_rect; params.focus_rect = gfx::Rect(30, 11, 0, 10); rwhv_mac_->SelectionBoundsChanged(params); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 1).ToNSRange(), + gfx::Range(0, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 2).ToNSRange(), + gfx::Range(2, 2).ToNSRange(), &rect, &actual_range)); } @@ -412,46 +412,46 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) { NSRect rect; // Make sure not crashing by passing NULL pointer instead of |actual_range|. EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, NULL)); // If there are no update from renderer, always returned caret position. NSRange actual_range; EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 1).ToNSRange(), + gfx::Range(0, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 0).ToNSRange(), + gfx::Range(1, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); // If the firstRectForCharacterRange is failed in renderer, empty rect vector // is sent. Make sure this does not crash. - rwhv_mac_->ImeCompositionRangeChanged(ui::Range(10, 12), + rwhv_mac_->ImeCompositionRangeChanged(gfx::Range(10, 12), std::vector<gfx::Rect>()); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(10, 11).ToNSRange(), + gfx::Range(10, 11).ToNSRange(), &rect, NULL)); const int kCompositionLength = 10; std::vector<gfx::Rect> composition_bounds; const int kCompositionStart = 3; - const ui::Range kCompositionRange(kCompositionStart, + const gfx::Range kCompositionRange(kCompositionStart, kCompositionStart + kCompositionLength); GenerateCompositionRectArray(kOrigin, kBoundsUnit, @@ -462,45 +462,45 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) { // Out of range requests will return caret position. EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(0, 0).ToNSRange(), + gfx::Range(0, 0).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 1).ToNSRange(), + gfx::Range(1, 1).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(1, 2).ToNSRange(), + gfx::Range(1, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(2, 2).ToNSRange(), + gfx::Range(2, 2).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(13, 14).ToNSRange(), + gfx::Range(13, 14).ToNSRange(), &rect, &actual_range)); EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange( - ui::Range(14, 15).ToNSRange(), + gfx::Range(14, 15).ToNSRange(), &rect, &actual_range)); for (int i = 0; i <= kCompositionLength; ++i) { for (int j = 0; j <= kCompositionLength - i; ++j) { - const ui::Range range(i, i + j); + const gfx::Range range(i, i + j); const gfx::Rect expected_rect = GetExpectedRect(kOrigin, kBoundsUnit, range, 0); - const NSRange request_range = ui::Range( + const NSRange request_range = gfx::Range( kCompositionStart + range.start(), kCompositionStart + range.end()).ToNSRange(); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange( request_range, &rect, &actual_range)); - EXPECT_EQ(ui::Range(request_range), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(request_range), gfx::Range(actual_range)); EXPECT_EQ(expected_rect, gfx::Rect(NSRectToCGRect(rect))); // Make sure not crashing by passing NULL pointer instead of @@ -520,7 +520,7 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) { const int kCompositionLength = 30; std::vector<gfx::Rect> composition_bounds; - const ui::Range kCompositionRange(0, kCompositionLength); + const gfx::Range kCompositionRange(0, kCompositionLength); // Set breaking point at 10 and 20. std::vector<size_t> break_points; break_points.push_back(10); @@ -533,113 +533,113 @@ TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) { rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds); // Range doesn't contain line breaking point. - ui::Range range; - range = ui::Range(5, 8); + gfx::Range range; + range = gfx::Range(5, 8); NSRange actual_range; EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(range, ui::Range(actual_range)); + EXPECT_EQ(range, gfx::Range(actual_range)); EXPECT_EQ( GetExpectedRect(kOrigin, kBoundsUnit, range, 0), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(15, 18); + range = gfx::Range(15, 18); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(range, ui::Range(actual_range)); + EXPECT_EQ(range, gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 8), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 1), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(25, 28); + range = gfx::Range(25, 28); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(range, ui::Range(actual_range)); + EXPECT_EQ(range, gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 8), 2), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 2), gfx::Rect(NSRectToCGRect(rect))); // Range contains line breaking point. - range = ui::Range(8, 12); + range = gfx::Range(8, 12); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(8, 10), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(8, 10), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(8, 10), 0), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 0), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(18, 22); + range = gfx::Range(18, 22); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(18, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(18, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(8, 10), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 1), gfx::Rect(NSRectToCGRect(rect))); // Start point is line breaking point. - range = ui::Range(10, 12); + range = gfx::Range(10, 12); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(10, 12), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(10, 12), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 2), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 1), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(20, 22); + range = gfx::Range(20, 22); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(20, 22), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(20, 22), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 2), 2), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 2), gfx::Rect(NSRectToCGRect(rect))); // End point is line breaking point. - range = ui::Range(5, 10); + range = gfx::Range(5, 10); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(5, 10), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(5, 10), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 10), 0), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 0), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(15, 20); + range = gfx::Range(15, 20); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(15, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(15, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(5, 10), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 1), gfx::Rect(NSRectToCGRect(rect))); // Start and end point are same line breaking point. - range = ui::Range(10, 10); + range = gfx::Range(10, 10); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(10, 10), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(10, 10), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 0), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 1), gfx::Rect(NSRectToCGRect(rect))); - range = ui::Range(20, 20); + range = gfx::Range(20, 20); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(20, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(20, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 0), 2), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 2), gfx::Rect(NSRectToCGRect(rect))); // Start and end point are different line breaking point. - range = ui::Range(10, 20); + range = gfx::Range(10, 20); EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(), &rect, &actual_range)); - EXPECT_EQ(ui::Range(10, 20), ui::Range(actual_range)); + EXPECT_EQ(gfx::Range(10, 20), gfx::Range(actual_range)); EXPECT_EQ( - GetExpectedRect(kOrigin, kBoundsUnit, ui::Range(0, 10), 1), + GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 10), 1), gfx::Rect(NSRectToCGRect(rect))); } diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win.cc b/chromium/content/browser/renderer_host/render_widget_host_view_win.cc index a6ee28b0189..de2aa656a0d 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_win.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_win.cc @@ -12,8 +12,9 @@ #include <map> #include <stack> +#include "base/basictypes.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/i18n/rtl.h" @@ -56,25 +57,26 @@ #include "third_party/WebKit/public/web/WebCompositionUnderline.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/skia/include/core/SkRegion.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" #include "ui/base/ime/composition_text.h" #include "ui/base/ime/win/imm32_manager.h" #include "ui/base/ime/win/tsf_input_scope.h" #include "ui/base/l10n/l10n_util_win.h" -#include "ui/base/text/text_elider.h" +#include "ui/base/sequential_id_generator.h" #include "ui/base/touch/touch_device.h" #include "ui/base/touch/touch_enabled.h" #include "ui/base/ui_base_switches.h" #include "ui/base/view_prop.h" -#include "ui/base/win/dpi.h" -#include "ui/base/win/hwnd_util.h" #include "ui/base/win/mouse_wheel_util.h" #include "ui/base/win/touch_input.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/gfx/canvas.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/screen.h" +#include "ui/gfx/text_elider.h" +#include "ui/gfx/win/dpi.h" +#include "ui/gfx/win/hwnd_util.h" #include "webkit/common/cursors/webcursor.h" #include "win8/util/win8_util.h" @@ -312,7 +314,7 @@ void GetScreenInfoForWindow(gfx::NativeViewId id, MONITORINFOEX monitor_info; monitor_info.cbSize = sizeof(MONITORINFOEX); - if (!GetMonitorInfo(monitor, &monitor_info)) + if (!base::win::GetMonitorInfoWrapper(monitor, &monitor_info)) return; DEVMODE dev_mode; @@ -323,7 +325,7 @@ void GetScreenInfoForWindow(gfx::NativeViewId id, WebKit::WebScreenInfo screen_info; screen_info.depth = dev_mode.dmBitsPerPel; screen_info.depthPerComponent = dev_mode.dmBitsPerPel / 3; // Assumes RGB - screen_info.deviceScaleFactor = ui::win::GetDeviceScaleFactor(); + screen_info.deviceScaleFactor = gfx::win::GetDeviceScaleFactor(); screen_info.isMonochrome = dev_mode.dmColor == DMCOLOR_MONOCHROME; screen_info.rect = gfx::Rect(monitor_info.rcMonitor); screen_info.availableRect = gfx::Rect(monitor_info.rcWork); @@ -355,8 +357,6 @@ class WebTouchState { bool is_changed() { return touch_event_.changedTouchesLength != 0; } private: - typedef std::map<unsigned int, int> MapType; - // Adds a touch point or returns NULL if there's not enough space. WebKit::WebTouchPoint* AddTouchPoint(TOUCHINPUT* touch_input); @@ -374,9 +374,7 @@ class WebTouchState { WebKit::WebTouchEvent touch_event_; const RenderWidgetHostViewWin* const window_; - // Maps OS touch Id's into an internal (WebKit-friendly) touch-id. - // WebKit expects small consecutive integers, starting at 0. - MapType touch_map_; + ui::SequentialIDGenerator id_generator_; DISALLOW_COPY_AND_ASSIGN(WebTouchState); }; @@ -395,7 +393,7 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) imm32_manager_(new ui::IMM32Manager), ime_notification_(false), capture_enter_key_(false), - is_hidden_(false), + is_hidden_(render_widget_host_->is_hidden()), about_to_validate_and_paint_(false), close_on_deactivate_(false), being_destroyed_(false), @@ -408,7 +406,7 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) can_compose_inline_(true), is_fullscreen_(false), ignore_mouse_movement_(true), - composition_range_(ui::Range::InvalidRange()), + composition_range_(gfx::Range::InvalidRange()), touch_state_(new WebTouchState(this)), pointer_down_context_(false), last_touch_location_(-1, -1), @@ -450,7 +448,7 @@ void RenderWidgetHostViewWin::InitAsFullscreen( gfx::Rect pos = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow( reference_host_view->GetNativeView()).bounds(); is_fullscreen_ = true; - DoPopupOrFullscreenInit(ui::GetWindowToParentTo(true), pos, 0); + DoPopupOrFullscreenInit(gfx::GetWindowToParentTo(true), pos, 0); } RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const { @@ -501,7 +499,7 @@ void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) { } void RenderWidgetHostViewWin::SetBounds(const gfx::Rect& rect) { - if (is_hidden_) + if (being_destroyed_) return; // No SWP_NOREDRAW as autofill popups can move and the underneath window @@ -574,7 +572,7 @@ void RenderWidgetHostViewWin::CleanupCompositorWindow() { if (!compositor_host_window_) return; - ui::SetWindowUserData(compositor_host_window_, NULL); + gfx::SetWindowUserData(compositor_host_window_, NULL); // Hide the compositor and parent it to the desktop rather than destroying // it immediately. The GPU process has a grace period to stop accessing the @@ -625,7 +623,7 @@ void RenderWidgetHostViewWin::Show() { } void RenderWidgetHostViewWin::Hide() { - if (!is_fullscreen_ && GetParent() == ui::GetWindowToParentTo(true)) { + if (!is_fullscreen_ && GetParent() == gfx::GetWindowToParentTo(true)) { LOG(WARNING) << "Hide() called twice in a row: " << this << ":" << GetParent(); return; @@ -643,7 +641,7 @@ bool RenderWidgetHostViewWin::IsShowing() { } gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const { - return ui::win::ScreenToDIPRect(GetPixelBounds()); + return gfx::win::ScreenToDIPRect(GetPixelBounds()); } gfx::Rect RenderWidgetHostViewWin::GetPixelBounds() const { @@ -690,8 +688,8 @@ void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) { void RenderWidgetHostViewWin::TextInputTypeChanged( ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) { + ui::TextInputMode input_mode, + bool can_compose_inline) { if (text_input_type_ != type || text_input_mode_ != input_mode || can_compose_inline_ != can_compose_inline) { @@ -725,7 +723,7 @@ void RenderWidgetHostViewWin::ImeCancelComposition() { } void RenderWidgetHostViewWin::ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) { composition_range_ = range; composition_character_bounds_ = character_bounds; @@ -762,6 +760,7 @@ void RenderWidgetHostViewWin::DidUpdateBackingStore( const gfx::Vector2d& scroll_delta, const std::vector<gfx::Rect>& copy_rects, const ui::LatencyInfo& latency_info) { + TRACE_EVENT0("content", "RenderWidgetHostViewWin::DidUpdateBackingStore"); software_latency_info_.MergeWith(latency_info); if (is_hidden_) return; @@ -772,7 +771,7 @@ void RenderWidgetHostViewWin::DidUpdateBackingStore( // surprisingly, this ordering matters. for (size_t i = 0; i < copy_rects.size(); ++i) { - gfx::Rect pixel_rect = ui::win::DIPToScreenRect(copy_rects[i]); + gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(copy_rects[i]); // Damage might not be DIP aligned. pixel_rect.Inset(-1, -1); RECT bounds = pixel_rect.ToRECT(); @@ -780,11 +779,12 @@ void RenderWidgetHostViewWin::DidUpdateBackingStore( } if (!scroll_rect.IsEmpty()) { - gfx::Rect pixel_rect = ui::win::DIPToScreenRect(scroll_rect); + TRACE_EVENT0("content", "ScrollWindowEx"); + gfx::Rect pixel_rect = gfx::win::DIPToScreenRect(scroll_rect); // Damage might not be DIP aligned. pixel_rect.Inset(-1, -1); RECT clip_rect = pixel_rect.ToRECT(); - float scale = ui::win::GetDeviceScaleFactor(); + float scale = gfx::win::GetDeviceScaleFactor(); int dx = static_cast<int>(scale * scroll_delta.x()); int dy = static_cast<int>(scale * scroll_delta.y()); ScrollWindowEx(dx, dy, NULL, &clip_rect, NULL, NULL, SW_INVALIDATE); @@ -806,7 +806,7 @@ bool RenderWidgetHostViewWin::CanSubscribeFrame() const { void RenderWidgetHostViewWin::WillWmDestroy() { CleanupCompositorWindow(); - if (base::win::IsTSFAwareRequired() && GetFocus() == m_hWnd) + if (base::win::IsTSFAwareRequired()) ui::TSFBridge::GetInstance()->RemoveFocusedClient(this); } @@ -843,7 +843,7 @@ void RenderWidgetHostViewWin::SetTooltipText(const string16& tooltip_text) { // accidentally DOS the user with a mega tooltip (since Windows doesn't seem // to do this itself). const string16 new_tooltip_text = - ui::TruncateString(tooltip_text, kMaxTooltipLength); + gfx::TruncateString(tooltip_text, kMaxTooltipLength); if (new_tooltip_text != tooltip_text_) { tooltip_text_ = new_tooltip_text; @@ -883,7 +883,7 @@ void RenderWidgetHostViewWin::CopyFromCompositingSurface( if (dst_size.IsEmpty() || src_subrect.IsEmpty()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); accelerated_surface_->AsyncCopyTo(src_subrect, dst_size, callback); } @@ -902,7 +902,7 @@ void RenderWidgetHostViewWin::CopyFromCompositingSurfaceToVideoFrame( if (src_subrect.IsEmpty()) return; - scoped_callback_runner.Release(); + ignore_result(scoped_callback_runner.Release()); accelerated_surface_->AsyncCopyToVideoFrame(src_subrect, target, callback); } @@ -1018,7 +1018,7 @@ void RenderWidgetHostViewWin::InsertText(const string16& text) { } if (render_widget_host_) render_widget_host_->ImeConfirmComposition(text, - ui::Range::InvalidRange(), + gfx::Range::InvalidRange(), false); } @@ -1048,7 +1048,7 @@ ui::TextInputMode RenderWidgetHostViewWin::GetTextInputMode() const { NOTREACHED(); return ui::TEXT_INPUT_MODE_DEFAULT; } - return ui::TEXT_INPUT_MODE_DEFAULT; + return text_input_mode_; } bool RenderWidgetHostViewWin::CanComposeInline() const { @@ -1096,7 +1096,7 @@ bool RenderWidgetHostViewWin::HasCompositionText() { return false; } -bool RenderWidgetHostViewWin::GetTextRange(ui::Range* range) { +bool RenderWidgetHostViewWin::GetTextRange(gfx::Range* range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1106,7 +1106,7 @@ bool RenderWidgetHostViewWin::GetTextRange(ui::Range* range) { return false; } -bool RenderWidgetHostViewWin::GetCompositionTextRange(ui::Range* range) { +bool RenderWidgetHostViewWin::GetCompositionTextRange(gfx::Range* range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1116,7 +1116,7 @@ bool RenderWidgetHostViewWin::GetCompositionTextRange(ui::Range* range) { return false; } -bool RenderWidgetHostViewWin::GetSelectionRange(ui::Range* range) { +bool RenderWidgetHostViewWin::GetSelectionRange(gfx::Range* range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1126,7 +1126,7 @@ bool RenderWidgetHostViewWin::GetSelectionRange(ui::Range* range) { return false; } -bool RenderWidgetHostViewWin::SetSelectionRange(const ui::Range& range) { +bool RenderWidgetHostViewWin::SetSelectionRange(const gfx::Range& range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1136,7 +1136,7 @@ bool RenderWidgetHostViewWin::SetSelectionRange(const ui::Range& range) { return false; } -bool RenderWidgetHostViewWin::DeleteRange(const ui::Range& range) { +bool RenderWidgetHostViewWin::DeleteRange(const gfx::Range& range) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; @@ -1146,13 +1146,13 @@ bool RenderWidgetHostViewWin::DeleteRange(const ui::Range& range) { return false; } -bool RenderWidgetHostViewWin::GetTextFromRange(const ui::Range& range, +bool RenderWidgetHostViewWin::GetTextFromRange(const gfx::Range& range, string16* text) { if (!base::win::IsTSFAwareRequired()) { NOTREACHED(); return false; } - ui::Range selection_text_range(selection_text_offset_, + gfx::Range selection_text_range(selection_text_offset_, selection_text_offset_ + selection_text_.length()); if (!selection_text_range.Contains(range)) { text->clear(); @@ -1313,7 +1313,7 @@ void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) { if (backing_store) { gfx::Rect bitmap_rect(gfx::Point(), - ui::win::DIPToScreenSize(backing_store->size())); + gfx::win::DIPToScreenSize(backing_store->size())); bool manage_colors = BackingStoreWin::ColorManagementEnabled(); if (manage_colors) @@ -1667,7 +1667,7 @@ LRESULT RenderWidgetHostViewWin::OnImeComposition( ui::CompositionText composition; if (imm32_manager_->GetResult(m_hWnd, lparam, &composition.text)) { render_widget_host_->ImeConfirmComposition( - composition.text, ui::Range::InvalidRange(), false); + composition.text, gfx::Range::InvalidRange(), false); imm32_manager_->ResetComposition(m_hWnd); // Fall though and try reading the composition string. // Japanese IMEs send a message containing both GCS_RESULTSTR and @@ -1679,7 +1679,7 @@ LRESULT RenderWidgetHostViewWin::OnImeComposition( if (imm32_manager_->GetComposition(m_hWnd, lparam, &composition)) { // TODO(suzhe): due to a bug of webkit, we can't use selection range with // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788 - composition.selection = ui::Range(composition.selection.end()); + composition.selection = gfx::Range(composition.selection.end()); // TODO(suzhe): convert both renderer_host and renderer to use // ui::CompositionText. @@ -2001,7 +2001,7 @@ LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, if (render_widget_host_) { WebKit::WebMouseWheelEvent wheel_event = WebMouseWheelEventBuilder::Build(m_hWnd, message, wparam, lparam); - float scale = ui::win::GetDeviceScaleFactor(); + float scale = gfx::win::GetDeviceScaleFactor(); wheel_event.x /= scale; wheel_event.y /= scale; wheel_event.deltaX /= scale; @@ -2014,7 +2014,9 @@ LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, } WebTouchState::WebTouchState(const RenderWidgetHostViewWin* window) - : window_(window) { } + : window_(window), + id_generator_(0) { +} size_t WebTouchState::UpdateTouchPoints( TOUCHINPUT* points, size_t count) { @@ -2114,22 +2116,12 @@ size_t WebTouchState::UpdateTouchPoints( } void WebTouchState::RemoveExpiredMappings() { - WebTouchState::MapType new_map; - for (MapType::iterator it = touch_map_.begin(); - it != touch_map_.end(); - ++it) { - WebKit::WebTouchPoint* point = touch_event_.touches; - WebKit::WebTouchPoint* end = point + touch_event_.touchesLength; - while (point < end) { - if ((point->id == it->second) && - (point->state != WebKit::WebTouchPoint::StateReleased)) { - new_map.insert(*it); - break; - } - point++; - } + WebKit::WebTouchPoint* point = touch_event_.touches; + WebKit::WebTouchPoint* end = point + touch_event_.touchesLength; + for (; point < end; ++point) { + if (point->state == WebKit::WebTouchPoint::StateReleased) + id_generator_.ReleaseGeneratedID(point->id); } - touch_map_.swap(new_map); } @@ -2167,18 +2159,20 @@ bool WebTouchState::UpdateTouchPoint( WebKit::WebTouchPoint* touch_point, TOUCHINPUT* touch_input) { CPoint coordinates( - TOUCH_COORD_TO_PIXEL(touch_input->x) / ui::win::GetUndocumentedDPIScale(), - TOUCH_COORD_TO_PIXEL(touch_input->y) / ui::win::GetUndocumentedDPIScale()); + TOUCH_COORD_TO_PIXEL(touch_input->x) / + gfx::win::GetUndocumentedDPITouchScale(), + TOUCH_COORD_TO_PIXEL(touch_input->y) / + gfx::win::GetUndocumentedDPITouchScale()); int radius_x = 1; int radius_y = 1; if (touch_input->dwMask & TOUCHINPUTMASKF_CONTACTAREA) { // Some touch drivers send a contact area of "-1", yet flag it as valid. radius_x = std::max(1, static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cxContact) / - ui::win::GetUndocumentedDPIScale())); + gfx::win::GetUndocumentedDPITouchScale())); radius_y = std::max(1, static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cyContact) / - ui::win::GetUndocumentedDPIScale())); + gfx::win::GetUndocumentedDPITouchScale())); } // Detect and exclude stationary moves. @@ -2194,7 +2188,7 @@ bool WebTouchState::UpdateTouchPoint( touch_point->screenPosition.x = coordinates.x; touch_point->screenPosition.y = coordinates.y; window_->ScreenToClient(&coordinates); - static float scale = ui::win::GetDeviceScaleFactor(); + static float scale = gfx::win::GetDeviceScaleFactor(); touch_point->position.x = coordinates.x / scale; touch_point->position.y = coordinates.y / scale; touch_point->radiusX = radius_x; @@ -2206,14 +2200,7 @@ bool WebTouchState::UpdateTouchPoint( // Find (or create) a mapping for _os_touch_id_. unsigned int WebTouchState::GetMappedTouch(unsigned int os_touch_id) { - MapType::iterator it = touch_map_.find(os_touch_id); - if (it != touch_map_.end()) - return it->second; - int next_value = 0; - for (it = touch_map_.begin(); it != touch_map_.end(); ++it) - next_value = std::max(next_value, it->second + 1); - touch_map_[os_touch_id] = next_value; - return next_value; + return id_generator_.GetGeneratedID(os_touch_id); } LRESULT RenderWidgetHostViewWin::OnTouchEvent(UINT message, WPARAM wparam, @@ -2241,8 +2228,10 @@ LRESULT RenderWidgetHostViewWin::OnTouchEvent(UINT message, WPARAM wparam, if (total == 1 && (points[0].dwFlags & TOUCHEVENTF_DOWN)) { pointer_down_context_ = true; last_touch_location_ = gfx::Point( - TOUCH_COORD_TO_PIXEL(points[0].x) / ui::win::GetUndocumentedDPIScale(), - TOUCH_COORD_TO_PIXEL(points[0].y) / ui::win::GetUndocumentedDPIScale()); + TOUCH_COORD_TO_PIXEL(points[0].x) / + gfx::win::GetUndocumentedDPITouchScale(), + TOUCH_COORD_TO_PIXEL(points[0].y) / + gfx::win::GetUndocumentedDPITouchScale()); } bool should_forward = render_widget_host_->ShouldForwardTouchEvent() && @@ -2311,7 +2300,7 @@ LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT message, ::ScreenToClient(m_hWnd, &cursor_pos); HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos); if (::IsWindow(child_window) && child_window != m_hWnd) { - if (ui::GetClassName(child_window) == kWrapperNativeWindowClassName) + if (gfx::GetClassName(child_window) == kWrapperNativeWindowClassName) child_window = ::GetWindow(child_window, GW_CHILD); ::SetFocus(child_window); @@ -2372,10 +2361,10 @@ LRESULT RenderWidgetHostViewWin::OnMoveOrSize( return 0; } -void RenderWidgetHostViewWin::OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params) { +void RenderWidgetHostViewWin::OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) { CreateBrowserAccessibilityManagerIfNeeded(); - GetBrowserAccessibilityManager()->OnAccessibilityNotifications(params); + GetBrowserAccessibilityManager()->OnAccessibilityEvents(params); } bool RenderWidgetHostViewWin::LockMouse() { @@ -2457,7 +2446,7 @@ static void PaintCompositorHostWindow(HWND hWnd) { BeginPaint(hWnd, &paint); RenderWidgetHostViewWin* win = static_cast<RenderWidgetHostViewWin*>( - ui::GetWindowUserData(hWnd)); + gfx::GetWindowUserData(hWnd)); // Trigger composite to rerender window. if (win) win->AcceleratedPaint(paint.hdc); @@ -2506,7 +2495,7 @@ gfx::Rect RenderWidgetHostViewWin::GetBoundsInRootWindow() { GetSystemMetrics(SM_CYSIZEFRAME)); } - return ui::win::ScreenToDIPRect(rect); + return gfx::win::ScreenToDIPRect(rect); } // Creates a HWND within the RenderWidgetHostView that will serve as a host @@ -2556,9 +2545,9 @@ gfx::GLSurfaceHandle RenderWidgetHostViewWin::GetCompositingSurface() { MAKEINTATOM(atom), 0, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED, 0, 0, width, height, m_hWnd, 0, instance, 0); - ui::CheckWindowCreated(compositor_host_window_); + gfx::CheckWindowCreated(compositor_host_window_); - ui::SetWindowUserData(compositor_host_window_, this); + gfx::SetWindowUserData(compositor_host_window_, this); gfx::GLSurfaceHandle surface_handle(compositor_host_window_, gfx::NATIVE_TRANSPORT); @@ -2754,6 +2743,8 @@ void RenderWidgetHostViewWin::OnFinalMessage(HWND window) { } if (render_widget_host_) render_widget_host_->ViewDestroyed(); + if (base::win::IsTSFAwareRequired()) + ui::TSFBridge::GetInstance()->RemoveFocusedClient(this); delete this; } @@ -2887,7 +2878,7 @@ void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message, return; } - gfx::Point point = ui::win::ScreenToDIPPoint( + gfx::Point point = gfx::win::ScreenToDIPPoint( gfx::Point(static_cast<short>(LOWORD(lparam)), static_cast<short>(HIWORD(lparam)))); lparam = MAKELPARAM(point.x(), point.y()); @@ -2966,7 +2957,7 @@ void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd, const gfx::Rect& pos, DWORD ex_style) { Create(parent_hwnd, NULL, NULL, WS_POPUP, ex_style); - gfx::Rect screen_rect = ui::win::DIPToScreenRect(pos); + gfx::Rect screen_rect = gfx::win::DIPToScreenRect(pos); MoveWindow(screen_rect.x(), screen_rect.y(), screen_rect.width(), screen_rect.height(), TRUE); ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA); @@ -3180,7 +3171,7 @@ void RenderWidgetHostViewWin::UpdateInputScopeIfNecessary( return; ui::tsf_inputscope::SetInputScopeForTsfUnawareWindow( - m_hWnd, text_input_type, ui::TEXT_INPUT_MODE_DEFAULT); + m_hWnd, text_input_type, text_input_mode_); } //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win.h b/chromium/content/browser/renderer_host/render_widget_host_view_win.h index 52030d78f39..6650ae4ba8b 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_win.h +++ b/chromium/content/browser/renderer_host/render_widget_host_view_win.h @@ -182,14 +182,14 @@ class RenderWidgetHostViewWin virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; virtual void SetIsLoading(bool is_loading) OVERRIDE; virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE; + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE; virtual void SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params& params) OVERRIDE; virtual void ScrollOffsetChanged() OVERRIDE; virtual void ImeCancelComposition() OVERRIDE; virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE; virtual void DidUpdateBackingStore( const gfx::Rect& scroll_rect, @@ -233,8 +233,8 @@ class RenderWidgetHostViewWin virtual void AcceleratedSurfaceSuspend() OVERRIDE; virtual void AcceleratedSurfaceRelease() OVERRIDE; virtual bool HasAcceleratedSurface(const gfx::Size& desired_size) OVERRIDE; - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& params + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params ) OVERRIDE; virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; @@ -277,12 +277,12 @@ class RenderWidgetHostViewWin virtual bool GetCompositionCharacterBounds(uint32 index, gfx::Rect* rect) OVERRIDE; virtual bool HasCompositionText() OVERRIDE; - virtual bool GetTextRange(ui::Range* range) OVERRIDE; - virtual bool GetCompositionTextRange(ui::Range* range) OVERRIDE; - virtual bool GetSelectionRange(ui::Range* range) OVERRIDE; - virtual bool SetSelectionRange(const ui::Range& range) OVERRIDE; - virtual bool DeleteRange(const ui::Range& range) OVERRIDE; - virtual bool GetTextFromRange(const ui::Range& range, + virtual bool GetTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetCompositionTextRange(gfx::Range* range) OVERRIDE; + virtual bool GetSelectionRange(gfx::Range* range) OVERRIDE; + virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; + virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; + virtual bool GetTextFromRange(const gfx::Range& range, string16* text) OVERRIDE; virtual void OnInputMethodChanged() OVERRIDE; virtual bool ChangeTextDirectionAndLayoutAlignment( @@ -572,7 +572,7 @@ class RenderWidgetHostViewWin // back, we regard the mouse movement as (0, 0). bool ignore_mouse_movement_; - ui::Range composition_range_; + gfx::Range composition_range_; // The current composition character bounds. std::vector<gfx::Rect> composition_character_bounds_; diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc index 76070bc70e5..fc73601fb6c 100644 --- a/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc +++ b/chromium/content/browser/renderer_host/render_widget_host_view_win_browsertest.cc @@ -10,11 +10,11 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" -#include "content/public/test/test_utils.h" #include "content/public/test/browser_test_utils.h" -#include "content/shell/shell.h" -#include "content/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" +#include "content/test/content_browser_test_utils.h" #include "ui/base/ime/composition_text.h" #include "ui/base/ime/text_input_type.h" #include "ui/base/ime/win/imm32_manager.h" @@ -87,16 +87,16 @@ IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewWinBrowserTest, MockIMM32Manager* mock = new MockIMM32Manager(); mock->Reset(); view_->imm32_manager_.reset(mock); - view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, false, - ui::TEXT_INPUT_MODE_EMAIL); + view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, + ui::TEXT_INPUT_MODE_EMAIL, false); EXPECT_EQ(1, mock->call_count()); EXPECT_EQ(view_->m_hWnd, mock->window_handle()); EXPECT_EQ(ui::TEXT_INPUT_MODE_EMAIL, mock->input_mode()); mock->Reset(); - view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, false, - ui::TEXT_INPUT_MODE_EMAIL); + view_->TextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, + ui::TEXT_INPUT_MODE_EMAIL, false); EXPECT_EQ(0, mock->call_count()); } diff --git a/chromium/content/browser/renderer_host/smooth_scroll_calculator.cc b/chromium/content/browser/renderer_host/smooth_scroll_calculator.cc deleted file mode 100644 index 2538a188354..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_calculator.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2013 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/browser/renderer_host/smooth_scroll_calculator.h" - -namespace content { - -SmoothScrollCalculator::SmoothScrollCalculator() { -} - -SmoothScrollCalculator::~SmoothScrollCalculator() { -} - -double SmoothScrollCalculator::GetScrollDelta( - base::TimeTicks now, base::TimeDelta desired_interval) { - double position_delta = 10; - if (!last_tick_time_.is_null()) { - double velocity = 10 / desired_interval.InMillisecondsF(); - double time_delta = (now - last_tick_time_).InMillisecondsF(); - position_delta = velocity * time_delta; - } - - last_tick_time_ = now; - return position_delta; -} - -} // namespace content diff --git a/chromium/content/browser/renderer_host/smooth_scroll_calculator.h b/chromium/content/browser/renderer_host/smooth_scroll_calculator.h deleted file mode 100644 index 1427416be41..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_calculator.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2013 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_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_ -#define CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_ - -#include "base/time/time.h" - -namespace content { - -// An utility class to calculate the delta for smooth scroll gesture -// events. -class SmoothScrollCalculator { - public: - SmoothScrollCalculator(); - ~SmoothScrollCalculator(); - - double GetScrollDelta(base::TimeTicks now, base::TimeDelta desired_interval); - - private: - base::TimeTicks last_tick_time_; - - DISALLOW_COPY_AND_ASSIGN(SmoothScrollCalculator); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_ diff --git a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.cc b/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.cc deleted file mode 100644 index ca9dfae9b78..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2013 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/browser/renderer_host/smooth_scroll_gesture_controller.h" - -#include "base/debug/trace_event.h" -#include "base/message_loop/message_loop.h" -#include "content/common/view_messages.h" -#include "content/port/browser/render_widget_host_view_port.h" -#include "content/port/browser/smooth_scroll_gesture.h" -#include "content/public/browser/render_widget_host.h" - -namespace content { - -namespace { - -// How many milliseconds apart synthetic scroll messages should be sent. -const int kSyntheticScrollMessageIntervalMs = 7; - -} // namespace - -SmoothScrollGestureController::SmoothScrollGestureController() - : rwh_(NULL) { -} - -SmoothScrollGestureController::~SmoothScrollGestureController() { -} - -void SmoothScrollGestureController::BeginSmoothScroll( - RenderWidgetHostViewPort* view, - const ViewHostMsg_BeginSmoothScroll_Params& params) { - if (pending_smooth_scroll_gesture_.get()) - return; - - rwh_ = view->GetRenderWidgetHost(); - pending_smooth_scroll_gesture_ = view->CreateSmoothScrollGesture( - params.scroll_down, - params.pixels_to_scroll, - params.mouse_event_x, - params.mouse_event_y); - - timer_.Start(FROM_HERE, GetSyntheticScrollMessageInterval(), this, - &SmoothScrollGestureController::OnTimer); -} - -base::TimeDelta - SmoothScrollGestureController::GetSyntheticScrollMessageInterval() const { - return base::TimeDelta::FromMilliseconds(kSyntheticScrollMessageIntervalMs); -} - -void SmoothScrollGestureController::OnTimer() { - base::TimeTicks now = base::TimeTicks::Now(); - if (!pending_smooth_scroll_gesture_->ForwardInputEvents(now, rwh_)) { - timer_.Stop(); - pending_smooth_scroll_gesture_ = NULL; - rwh_->Send(new ViewMsg_SmoothScrollCompleted(rwh_->GetRoutingID())); - } -} - -} // namespace content diff --git a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.h b/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.h deleted file mode 100644 index 1477a171e6a..00000000000 --- a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2013 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_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_ -#define CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_ - -#include <map> - -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "base/timer/timer.h" -#include "content/common/content_export.h" - -struct ViewHostMsg_BeginSmoothScroll_Params; - -namespace content { - -class RenderWidgetHost; -class RenderWidgetHostViewPort; -class SmoothScrollGesture; - -// Controls SmoothScrollGestures, used to inject synthetic events -// for performance test harness. -class CONTENT_EXPORT SmoothScrollGestureController { - public: - SmoothScrollGestureController(); - ~SmoothScrollGestureController(); - - // Initiates a synthetic event stream. - void BeginSmoothScroll(RenderWidgetHostViewPort* view, - const ViewHostMsg_BeginSmoothScroll_Params& params); - - base::TimeDelta GetSyntheticScrollMessageInterval() const; - - private: - // Called periodically to advance the active scroll gesture after being - // initiated by OnBeginSmoothScroll. - void OnTimer(); - - base::RepeatingTimer<SmoothScrollGestureController> timer_; - - RenderWidgetHost* rwh_; - - scoped_refptr<SmoothScrollGesture> pending_smooth_scroll_gesture_; - - DISALLOW_COPY_AND_ASSIGN(SmoothScrollGestureController); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_ diff --git a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc index 7f122df2c73..677c281932c 100644 --- a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc +++ b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.cc @@ -30,14 +30,13 @@ const size_t kMaxSocketStreamHosts = 16 * 1024; SocketStreamDispatcherHost::SocketStreamDispatcherHost( int render_process_id, - ResourceMessageFilter::URLRequestContextSelector* selector, + const GetRequestContextCallback& request_context_callback, ResourceContext* resource_context) : render_process_id_(render_process_id), - url_request_context_selector_(selector), + request_context_callback_(request_context_callback), resource_context_(resource_context), weak_ptr_factory_(this), on_shutdown_(false) { - DCHECK(selector); net::WebSocketJob::EnsureInit(); } @@ -263,8 +262,7 @@ void SocketStreamDispatcherHost::DeleteSocketStreamHost(int socket_id) { } net::URLRequestContext* SocketStreamDispatcherHost::GetURLRequestContext() { - return url_request_context_selector_->GetRequestContext( - ResourceType::SUB_RESOURCE); + return request_context_callback_.Run(ResourceType::SUB_RESOURCE); } void SocketStreamDispatcherHost::Shutdown() { diff --git a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h index 732923e690f..958eada3a57 100644 --- a/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h +++ b/chromium/content/browser/renderer_host/socket_stream_dispatcher_host.h @@ -7,9 +7,9 @@ #include <vector> +#include "base/callback_forward.h" #include "base/id_map.h" #include "base/memory/weak_ptr.h" -#include "content/browser/loader/resource_message_filter.h" #include "content/browser/ssl/ssl_error_handler.h" #include "content/public/browser/browser_message_filter.h" #include "net/socket_stream/socket_stream.h" @@ -32,9 +32,11 @@ class SocketStreamDispatcherHost public net::SocketStream::Delegate, public SSLErrorHandler::Delegate { public: + typedef base::Callback<net::URLRequestContext*(ResourceType::Type)> + GetRequestContextCallback; SocketStreamDispatcherHost( int render_process_id, - ResourceMessageFilter::URLRequestContextSelector* selector, + const GetRequestContextCallback& request_context_callback, ResourceContext* resource_context); // BrowserMessageFilter: @@ -84,8 +86,7 @@ class SocketStreamDispatcherHost IDMap<SocketStreamHost> hosts_; int render_process_id_; - const scoped_ptr<ResourceMessageFilter::URLRequestContextSelector> - url_request_context_selector_; + GetRequestContextCallback request_context_callback_; ResourceContext* resource_context_; base::WeakPtrFactory<SocketStreamDispatcherHost> weak_ptr_factory_; diff --git a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc index b563480efd9..d382cd57565 100644 --- a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc +++ b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.cc @@ -15,7 +15,7 @@ #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" -#include "ui/gl/android/surface_texture_bridge.h" +#include "ui/gl/android/surface_texture.h" namespace content { @@ -26,7 +26,7 @@ static const uint32 kGLTextureExternalOES = 0x8D65; class SurfaceRefAndroid : public GpuSurfaceTracker::SurfaceRef { public: SurfaceRefAndroid( - const scoped_refptr<gfx::SurfaceTextureBridge>& surface, + const scoped_refptr<gfx::SurfaceTexture>& surface, ANativeWindow* window) : surface_(surface), window_(window) { @@ -39,7 +39,7 @@ class SurfaceRefAndroid : public GpuSurfaceTracker::SurfaceRef { ANativeWindow_release(window_); } - scoped_refptr<gfx::SurfaceTextureBridge> surface_; + scoped_refptr<gfx::SurfaceTexture> surface_; ANativeWindow* window_; }; @@ -61,7 +61,7 @@ scoped_refptr<cc::Layer> SurfaceTextureTransportClient::Initialize() { // Use a SurfaceTexture to stream frames to the UI thread. video_layer_ = cc::VideoLayer::Create(this); - surface_texture_ = new gfx::SurfaceTextureBridge(0); + surface_texture_ = new gfx::SurfaceTexture(0); surface_texture_->SetFrameAvailableCallback( base::Bind( &SurfaceTextureTransportClient::OnSurfaceTextureFrameAvailable, diff --git a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h index 83d917552dc..5a000bb246e 100644 --- a/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h +++ b/chromium/content/browser/renderer_host/surface_texture_transport_client_android.h @@ -21,7 +21,7 @@ class VideoLayer; } namespace gfx { -class SurfaceTextureBridge; +class SurfaceTexture; } namespace content { @@ -45,7 +45,7 @@ class SurfaceTextureTransportClient : public cc::VideoFrameProvider { void OnSurfaceTextureFrameAvailable(); scoped_refptr<cc::VideoLayer> video_layer_; - scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_; + scoped_refptr<gfx::SurfaceTexture> surface_texture_; ANativeWindow* window_; scoped_refptr<media::VideoFrame> video_frame_; uint32 texture_id_; diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_calculator.cc b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.cc new file mode 100644 index 00000000000..9816df93b48 --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.cc @@ -0,0 +1,37 @@ +// Copyright 2013 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/browser/renderer_host/synthetic_gesture_calculator.h" + + +namespace { + +const float kDefaultPositionDelta = 10.0f; + +} + + +namespace content { + +SyntheticGestureCalculator::SyntheticGestureCalculator() { +} + +SyntheticGestureCalculator::~SyntheticGestureCalculator() { +} + +float SyntheticGestureCalculator::GetDelta( + base::TimeTicks now, base::TimeDelta desired_interval) { + float position_delta = kDefaultPositionDelta; + if (!last_tick_time_.is_null()) { + float velocity = kDefaultPositionDelta / + (float)desired_interval.InMillisecondsF(); + float time_delta = (now - last_tick_time_).InMillisecondsF(); + position_delta = velocity * time_delta; + } + + last_tick_time_ = now; + return position_delta; +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_calculator.h b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.h new file mode 100644 index 00000000000..c2af4978ac4 --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_calculator.h @@ -0,0 +1,28 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_ +#define CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_ + +#include "base/time/time.h" + +namespace content { + +// A utility class to calculate the delta for synthetic gesture events. +class SyntheticGestureCalculator { + public: + SyntheticGestureCalculator(); + ~SyntheticGestureCalculator(); + + float GetDelta(base::TimeTicks now, base::TimeDelta desired_interval); + + private: + base::TimeTicks last_tick_time_; + + DISALLOW_COPY_AND_ASSIGN(SyntheticGestureCalculator); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_ diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_controller.cc b/chromium/content/browser/renderer_host/synthetic_gesture_controller.cc new file mode 100644 index 00000000000..46c4329da6b --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_controller.cc @@ -0,0 +1,84 @@ +// Copyright 2013 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/browser/renderer_host/synthetic_gesture_controller.h" + +#include "base/debug/trace_event.h" +#include "base/message_loop/message_loop.h" +#include "content/common/view_messages.h" +#include "content/port/browser/render_widget_host_view_port.h" +#include "content/port/browser/synthetic_gesture.h" +#include "content/public/browser/render_widget_host.h" + +namespace content { + +namespace { + +// How many milliseconds apart synthetic scroll messages should be sent. +const int kSyntheticGestureMessageIntervalMs = 7; + +} // namespace + +SyntheticGestureController::SyntheticGestureController() + : rwh_(NULL) { +} + +SyntheticGestureController::~SyntheticGestureController() { +} + +void SyntheticGestureController::BeginSmoothScroll( + RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginSmoothScroll_Params& params) { + if (pending_synthetic_gesture_.get()) + return; + + rwh_ = view->GetRenderWidgetHost(); + pending_synthetic_gesture_ = view->CreateSmoothScrollGesture( + params.scroll_down, + params.pixels_to_scroll, + params.mouse_event_x, + params.mouse_event_y); + + TRACE_EVENT_ASYNC_BEGIN0("benchmark", "SyntheticGestureController::running", + pending_synthetic_gesture_); + timer_.Start(FROM_HERE, GetSyntheticGestureMessageInterval(), this, + &SyntheticGestureController::OnTimer); +} + +void SyntheticGestureController::BeginPinch( + RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginPinch_Params& params) { + if (pending_synthetic_gesture_.get()) + return; + + rwh_ = view->GetRenderWidgetHost(); + pending_synthetic_gesture_ = view->CreatePinchGesture( + params.zoom_in, + params.pixels_to_move, + params.anchor_x, + params.anchor_y); + + TRACE_EVENT_ASYNC_BEGIN0("benchmark", "SyntheticGestureController::running", + pending_synthetic_gesture_); + timer_.Start(FROM_HERE, GetSyntheticGestureMessageInterval(), this, + &SyntheticGestureController::OnTimer); +} + +base::TimeDelta + SyntheticGestureController::GetSyntheticGestureMessageInterval() const { + return base::TimeDelta::FromMilliseconds(kSyntheticGestureMessageIntervalMs); +} + +void SyntheticGestureController::OnTimer() { + base::TimeTicks now = base::TimeTicks::Now(); + if (!pending_synthetic_gesture_->ForwardInputEvents(now, rwh_)) { + timer_.Stop(); + TRACE_EVENT_ASYNC_END0("benchmark", "SyntheticGestureController::running", + pending_synthetic_gesture_); + pending_synthetic_gesture_ = NULL; + rwh_->Send(new ViewMsg_SyntheticGestureCompleted(rwh_->GetRoutingID())); + } +} + +} // namespace content diff --git a/chromium/content/browser/renderer_host/synthetic_gesture_controller.h b/chromium/content/browser/renderer_host/synthetic_gesture_controller.h new file mode 100644 index 00000000000..8826aaddf3a --- /dev/null +++ b/chromium/content/browser/renderer_host/synthetic_gesture_controller.h @@ -0,0 +1,57 @@ +// Copyright 2013 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_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_ + +#include <map> + +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "base/timer/timer.h" +#include "content/common/content_export.h" + +struct ViewHostMsg_BeginPinch_Params; +struct ViewHostMsg_BeginSmoothScroll_Params; + +namespace content { + +class RenderWidgetHost; +class RenderWidgetHostViewPort; +class SyntheticGesture; + +// Controls SyntheticGestures, used to inject synthetic events +// for performance test harness. +class CONTENT_EXPORT SyntheticGestureController { + public: + SyntheticGestureController(); + ~SyntheticGestureController(); + + // Initiates a synthetic event stream to simulate a smooth scroll. + void BeginSmoothScroll(RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginSmoothScroll_Params& params); + + // Initiates a synthetic event stream to simulate a pinch-to-zoom. + void BeginPinch(RenderWidgetHostViewPort* view, + const ViewHostMsg_BeginPinch_Params& params); + + base::TimeDelta GetSyntheticGestureMessageInterval() const; + + private: + // Called periodically to advance the active scroll gesture after being + // initiated by OnBeginSmoothScroll. + void OnTimer(); + + base::RepeatingTimer<SyntheticGestureController> timer_; + + RenderWidgetHost* rwh_; + + scoped_refptr<SyntheticGesture> pending_synthetic_gesture_; + + DISALLOW_COPY_AND_ASSIGN(SyntheticGestureController); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_ diff --git a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc b/chromium/content/browser/renderer_host/synthetic_gesture_controller_unittest.cc index 95d26473ee1..7fb9ba9e601 100644 --- a/chromium/content/browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc +++ b/chromium/content/browser/renderer_host/synthetic_gesture_controller_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 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. @@ -6,11 +6,11 @@ #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" -#include "content/browser/renderer_host/smooth_scroll_gesture_controller.h" +#include "content/browser/renderer_host/synthetic_gesture_controller.h" #include "content/browser/renderer_host/test_render_view_host.h" #include "content/common/view_messages.h" #include "content/port/browser/render_widget_host_view_port.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/port/browser/synthetic_gesture.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,9 +26,9 @@ namespace content { namespace { -class MockSmoothScrollGesture : public SmoothScrollGesture { +class MockSyntheticGesture : public SyntheticGesture { public: - MockSmoothScrollGesture() : + MockSyntheticGesture() : called_(0) { } @@ -42,7 +42,7 @@ class MockSmoothScrollGesture : public SmoothScrollGesture { int called_; protected: - virtual ~MockSmoothScrollGesture() { + virtual ~MockSyntheticGesture() { } }; @@ -59,7 +59,7 @@ class MockRenderWidgetHost : public RenderWidgetHostImpl { RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id) - : RenderWidgetHostImpl(delegate, process, routing_id) { + : RenderWidgetHostImpl(delegate, process, routing_id, false) { } virtual ~MockRenderWidgetHost() {} }; @@ -73,10 +73,10 @@ class TestView : public TestRenderWidgetHostView { virtual ~TestView() {} // TestRenderWidgetHostView implementation: - virtual SmoothScrollGesture* CreateSmoothScrollGesture( + virtual SyntheticGesture* CreateSmoothScrollGesture( bool scroll_down, int pixels_to_scroll, int mouse_event_x, int mouse_event_y) OVERRIDE { - mock_gesture_ = new MockSmoothScrollGesture(); + mock_gesture_ = new MockSyntheticGesture(); return mock_gesture_; } @@ -84,14 +84,14 @@ class TestView : public TestRenderWidgetHostView { return rwh_; } - MockSmoothScrollGesture* mock_gesture_; + MockSyntheticGesture* mock_gesture_; }; -class SmoothScrollGestureControllerTest : public testing::Test { +class SyntheticGestureControllerTest : public testing::Test { public: - SmoothScrollGestureControllerTest() : process_(NULL) { + SyntheticGestureControllerTest() : process_(NULL) { } - virtual ~SmoothScrollGestureControllerTest() {} + virtual ~SyntheticGestureControllerTest() {} protected: // testing::Test implementation: @@ -131,7 +131,7 @@ class SmoothScrollGestureControllerTest : public testing::Test { base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::MessageLoop::QuitClosure(), TimeDelta::FromMilliseconds( - controller_.GetSyntheticScrollMessageInterval().InMilliseconds() * + controller_.GetSyntheticGestureMessageInterval().InMilliseconds() * 3)); base::MessageLoop::current()->Run(); } @@ -147,10 +147,10 @@ class SmoothScrollGestureControllerTest : public testing::Test { scoped_ptr<gfx::Screen> screen_; #endif - SmoothScrollGestureController controller_; + SyntheticGestureController controller_; }; -TEST_F(SmoothScrollGestureControllerTest, Tick) { +TEST_F(SyntheticGestureControllerTest, Tick) { ViewHostMsg_BeginSmoothScroll_Params params; params.scroll_down = true; params.pixels_to_scroll = 10; @@ -168,7 +168,7 @@ TEST_F(SmoothScrollGestureControllerTest, Tick) { EXPECT_LT(0, current_ticks); // Ensure it won't start another smooth scroll. - MockSmoothScrollGesture* original_gesture = view_->mock_gesture_; + MockSyntheticGesture* original_gesture = view_->mock_gesture_; controller_.BeginSmoothScroll(view_.get(), params); PostQuitMessageAndRun(); EXPECT_EQ(original_gesture, view_->mock_gesture_); diff --git a/chromium/content/browser/renderer_host/test_render_view_host.cc b/chromium/content/browser/renderer_host/test_render_view_host.cc index 129591cac7b..caf79cf8583 100644 --- a/chromium/content/browser/renderer_host/test_render_view_host.cc +++ b/chromium/content/browser/renderer_host/test_render_view_host.cc @@ -15,7 +15,6 @@ #include "content/public/browser/storage_partition.h" #include "content/public/common/content_client.h" #include "content/public/common/page_state.h" -#include "content/public/common/password_form.h" #include "content/test/test_web_contents.h" #include "media/base/video_frame.h" #include "ui/gfx/rect.h" @@ -42,7 +41,6 @@ void InitNavigateParams(ViewHostMsg_FrameNavigate_Params* params, params->should_update_history = false; params->searchable_form_url = GURL(); params->searchable_form_encoding = std::string(); - params->password_form = PasswordForm(); params->security_info = std::string(); params->gesture = NavigationGestureUser; params->was_within_same_page = false; @@ -214,14 +212,6 @@ void TestRenderWidgetHostView::SetClickthroughRegion(SkRegion* region) { } #endif -#if defined(OS_WIN) && defined(USE_AURA) -gfx::NativeViewAccessible -TestRenderWidgetHostView::AccessibleObjectFromChildId(long child_id) { - NOTIMPLEMENTED(); - return NULL; -} -#endif - bool TestRenderWidgetHostView::LockMouse() { return false; } @@ -247,7 +237,8 @@ TestRenderViewHost::TestRenderViewHost( widget_delegate, routing_id, main_frame_routing_id, - swapped_out), + swapped_out, + false /* hidden */), render_view_created_(false), delete_counter_(NULL), simulate_fetch_via_proxy_(false), @@ -333,7 +324,6 @@ void TestRenderViewHost::SendNavigateWithParameters( params.should_update_history = true; params.searchable_form_url = GURL(); params.searchable_form_encoding = std::string(); - params.password_form = PasswordForm(); params.security_info = std::string(); params.gesture = NavigationGestureUser; params.contents_mime_type = contents_mime_type_; diff --git a/chromium/content/browser/renderer_host/test_render_view_host.h b/chromium/content/browser/renderer_host/test_render_view_host.h index 37b46e2bef8..1eb7583bec0 100644 --- a/chromium/content/browser/renderer_host/test_render_view_host.h +++ b/chromium/content/browser/renderer_host/test_render_view_host.h @@ -99,12 +99,12 @@ class TestRenderWidgetHostView : public RenderWidgetHostViewBase { virtual void SetIsLoading(bool is_loading) OVERRIDE {} virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE {} virtual void TextInputTypeChanged(ui::TextInputType type, - bool can_compose_inline, - ui::TextInputMode input_mode) OVERRIDE {} + ui::TextInputMode input_mode, + bool can_compose_inline) OVERRIDE {} virtual void ImeCancelComposition() OVERRIDE {} #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) virtual void ImeCompositionRangeChanged( - const ui::Range& range, + const gfx::Range& range, const std::vector<gfx::Rect>& character_bounds) OVERRIDE {} #endif virtual void DidUpdateBackingStore( @@ -158,17 +158,12 @@ class TestRenderWidgetHostView : public RenderWidgetHostViewBase { bool has_horizontal_scrollbar) OVERRIDE { } virtual void SetScrollOffsetPinning( bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE { } - virtual void OnAccessibilityNotifications( - const std::vector<AccessibilityHostMsg_NotificationParams>& - params) OVERRIDE {} + virtual void OnAccessibilityEvents( + const std::vector<AccessibilityHostMsg_EventParams>& params) OVERRIDE {} virtual gfx::GLSurfaceHandle GetCompositingSurface() OVERRIDE; #if defined(OS_WIN) && !defined(USE_AURA) virtual void SetClickthroughRegion(SkRegion* region) OVERRIDE; #endif -#if defined(OS_WIN) && defined(USE_AURA) - virtual gfx::NativeViewAccessible AccessibleObjectFromChildId(long child_id) - OVERRIDE; -#endif virtual bool LockMouse() OVERRIDE; virtual void UnlockMouse() OVERRIDE; #if defined(OS_WIN) && defined(USE_AURA) diff --git a/chromium/content/browser/renderer_host/text_input_client_mac.mm b/chromium/content/browser/renderer_host/text_input_client_mac.mm index cdf56a1eb19..b9d900a704e 100644 --- a/chromium/content/browser/renderer_host/text_input_client_mac.mm +++ b/chromium/content/browser/renderer_host/text_input_client_mac.mm @@ -61,7 +61,7 @@ NSRect TextInputClientMac::GetFirstRectForRange(RenderWidgetHost* rwh, RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh); rwhi->Send( new TextInputClientMsg_FirstRectForCharacterRange(rwhi->GetRoutingID(), - ui::Range(range))); + gfx::Range(range))); // http://crbug.com/121917 base::ThreadRestrictions::ScopedAllowWait allow_wait; condition_.TimedWait(base::TimeDelta::FromMilliseconds(kWaitTimeout)); @@ -82,7 +82,7 @@ NSAttributedString* TextInputClientMac::GetAttributedSubstringFromRange( BeforeRequest(); RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh); rwhi->Send(new TextInputClientMsg_StringForRange(rwhi->GetRoutingID(), - ui::Range(range))); + gfx::Range(range))); // http://crbug.com/121917 base::ThreadRestrictions::ScopedAllowWait allow_wait; condition_.TimedWait(base::TimeDelta::FromMilliseconds(kWaitTimeout)); diff --git a/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm b/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm index 2a4c6d7f08d..0993018e8f4 100644 --- a/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm +++ b/chromium/content/browser/renderer_host/text_input_client_mac_unittest.mm @@ -42,7 +42,7 @@ class TextInputClientMacTest : public testing::Test { widget_(&delegate_, process_factory_.CreateRenderProcessHost( &browser_context_, NULL), - MSG_ROUTING_NONE), + MSG_ROUTING_NONE, false), thread_("TextInputClientMacTestThread") {} // Accessor for the TextInputClientMac instance. diff --git a/chromium/content/browser/renderer_host/text_input_client_message_filter.h b/chromium/content/browser/renderer_host/text_input_client_message_filter.h index 2a374df5c5e..5a1405ae5ff 100644 --- a/chromium/content/browser/renderer_host/text_input_client_message_filter.h +++ b/chromium/content/browser/renderer_host/text_input_client_message_filter.h @@ -9,11 +9,8 @@ #include "content/public/browser/browser_message_filter.h" namespace gfx { -class Rect; -} - -namespace ui { class Range; +class Rect; } namespace content { diff --git a/chromium/content/browser/renderer_host/text_input_client_message_filter.mm b/chromium/content/browser/renderer_host/text_input_client_message_filter.mm index cea0a347d70..7af5a8ff2bd 100644 --- a/chromium/content/browser/renderer_host/text_input_client_message_filter.mm +++ b/chromium/content/browser/renderer_host/text_input_client_message_filter.mm @@ -10,7 +10,7 @@ #include "content/common/text_input_client_messages.h" #include "content/public/browser/render_widget_host_view.h" #include "ipc/ipc_message_macros.h" -#include "ui/base/range/range.h" +#include "ui/gfx/range/range.h" #include "ui/gfx/rect.h" namespace content { diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc deleted file mode 100644 index c504f6c07c9..00000000000 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2013 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/browser/renderer_host/touch_smooth_scroll_gesture_android.h" - -#include "base/debug/trace_event.h" -#include "content/browser/renderer_host/render_widget_host_impl.h" -#include "jni/SmoothScroller_jni.h" - -namespace { -bool g_jni_initialized = false; - -void RegisterNativesIfNeeded(JNIEnv* env) { - if (!g_jni_initialized) { - content::RegisterNativesImpl(env); - g_jni_initialized = true; - } -} -} // namespace - -namespace content { - -TouchSmoothScrollGestureAndroid::TouchSmoothScrollGestureAndroid( - int pixels_to_scroll, - RenderWidgetHost* rwh, - base::android::ScopedJavaLocalRef<jobject> java_scroller) - : pixels_scrolled_(0), - has_started_(false), - has_sent_motion_up_(false), - pixels_to_scroll_(pixels_to_scroll), - rwh_(rwh), - java_scroller_(java_scroller) { - JNIEnv* env = base::android::AttachCurrentThread(); - RegisterNativesIfNeeded(env); -} - -TouchSmoothScrollGestureAndroid::~TouchSmoothScrollGestureAndroid() { -} - -bool TouchSmoothScrollGestureAndroid::ForwardInputEvents( - base::TimeTicks now, RenderWidgetHost* host) { - if (!has_started_) { - has_started_ = true; - JNIEnv* env = base::android::AttachCurrentThread(); - Java_SmoothScroller_start( - env, java_scroller_.obj(), reinterpret_cast<int>(this)); - } - - TRACE_COUNTER_ID1( - "gpu", "smooth_scroll_by_pixels_scrolled", this, pixels_scrolled_); - - return !has_sent_motion_up_; -} - -double TouchSmoothScrollGestureAndroid::GetScrollDelta( - JNIEnv* env, jobject obj, double scale) { - double delta = smooth_scroll_calculator_.GetScrollDelta( - base::TimeTicks::Now(), - RenderWidgetHostImpl::From(rwh_)->GetSyntheticScrollMessageInterval()) - * scale; - pixels_scrolled_ += delta; - return delta; -} - -bool TouchSmoothScrollGestureAndroid::HasFinished(JNIEnv* env, jobject obj) { - return pixels_scrolled_ >= pixels_to_scroll_; -} - -void TouchSmoothScrollGestureAndroid::SetHasSentMotionUp( - JNIEnv* env, jobject obj) { - has_sent_motion_up_ = true; -} - -} // namespace content diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h deleted file mode 100644 index 0b892670643..00000000000 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2013 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_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_ -#define CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_ - -#include "base/android/jni_android.h" -#include "base/time/time.h" -#include "content/browser/renderer_host/smooth_scroll_calculator.h" -#include "content/port/browser/smooth_scroll_gesture.h" - -namespace content { - -class ContentViewCore; -class RenderWidgetHost; - -class TouchSmoothScrollGestureAndroid : public SmoothScrollGesture { - public: - TouchSmoothScrollGestureAndroid( - int pixels_to_scroll, - RenderWidgetHost* rwh, - base::android::ScopedJavaLocalRef<jobject> java_scroller); - - // Called by the java side once the TimeAnimator ticks. - double GetScrollDelta(JNIEnv* env, jobject obj, double scale); - bool HasFinished(JNIEnv* env, jobject obj); - void SetHasSentMotionUp(JNIEnv* env, jobject obj); - - // SmoothScrollGesture - virtual bool ForwardInputEvents(base::TimeTicks now, - RenderWidgetHost* host) OVERRIDE; - - private: - virtual ~TouchSmoothScrollGestureAndroid(); - - SmoothScrollCalculator smooth_scroll_calculator_; - - int pixels_scrolled_; - bool has_started_; - bool has_sent_motion_up_; - - int pixels_to_scroll_; - RenderWidgetHost* rwh_; - base::android::ScopedJavaGlobalRef<jobject> java_scroller_; - - DISALLOW_COPY_AND_ASSIGN(TouchSmoothScrollGestureAndroid); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_ diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc index ffb1564ded1..5df932dfa3b 100644 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc +++ b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc @@ -6,8 +6,8 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "ui/aura/root_window.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/gfx/transform.h" namespace { @@ -53,8 +53,8 @@ bool TouchSmoothScrollGestureAura::ForwardInputEvents( return false; RenderWidgetHostImpl* host_impl = RenderWidgetHostImpl::From(host); - double position_delta = smooth_scroll_calculator_.GetScrollDelta(now, - host_impl->GetSyntheticScrollMessageInterval()); + float position_delta = synthetic_gesture_calculator_.GetDelta(now, + host_impl->GetSyntheticGestureMessageInterval()); if (pixels_scrolled_ == 0) { InjectTouchEvent(location_, ui::ET_TOUCH_PRESSED, window_); diff --git a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h index 779e02d6dd0..890b1467d68 100644 --- a/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h +++ b/chromium/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h @@ -6,8 +6,8 @@ #define CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_SCROLL_GESTURE_ #include "base/time/time.h" -#include "content/browser/renderer_host/smooth_scroll_calculator.h" -#include "content/port/browser/smooth_scroll_gesture.h" +#include "content/browser/renderer_host/synthetic_gesture_calculator.h" +#include "content/port/browser/synthetic_gesture.h" #include "ui/gfx/point.h" namespace aura { @@ -16,7 +16,7 @@ class Window; namespace content { -class TouchSmoothScrollGestureAura : public SmoothScrollGesture { +class TouchSmoothScrollGestureAura : public SyntheticGesture { public: TouchSmoothScrollGestureAura(bool scroll_down, int pixels_to_scroll, @@ -35,7 +35,7 @@ class TouchSmoothScrollGestureAura : public SmoothScrollGesture { int pixels_scrolled_; gfx::Point location_; aura::Window* window_; - SmoothScrollCalculator smooth_scroll_calculator_; + SyntheticGestureCalculator synthetic_gesture_calculator_; DISALLOW_COPY_AND_ASSIGN(TouchSmoothScrollGestureAura); }; diff --git a/chromium/content/browser/renderer_host/ui_events_helper.cc b/chromium/content/browser/renderer_host/ui_events_helper.cc index 89242946cab..78a7bff4f74 100644 --- a/chromium/content/browser/renderer_host/ui_events_helper.cc +++ b/chromium/content/browser/renderer_host/ui_events_helper.cc @@ -5,8 +5,8 @@ #include "content/browser/renderer_host/ui_events_helper.h" #include "third_party/WebKit/public/web/WebInputEvent.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_constants.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" namespace { diff --git a/chromium/content/browser/renderer_host/web_input_event_aura.cc b/chromium/content/browser/renderer_host/web_input_event_aura.cc index 8931f1f5d91..6bb5e5838af 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aura.cc +++ b/chromium/content/browser/renderer_host/web_input_event_aura.cc @@ -6,11 +6,57 @@ #include "content/browser/renderer_host/ui_events_helper.h" #include "ui/aura/window.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" namespace content { +#if defined(USE_X11) || defined(USE_OZONE) +// From third_party/WebKit/Source/web/gtk/WebInputEventFactory.cpp: +WebKit::WebUChar GetControlCharacter(int windows_key_code, bool shift) { + if (windows_key_code >= ui::VKEY_A && + windows_key_code <= ui::VKEY_Z) { + // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A + return windows_key_code - ui::VKEY_A + 1; + } + if (shift) { + // following graphics chars require shift key to input. + switch (windows_key_code) { + // ctrl-@ maps to \x00 (Null byte) + case ui::VKEY_2: + return 0; + // ctrl-^ maps to \x1E (Record separator, Information separator two) + case ui::VKEY_6: + return 0x1E; + // ctrl-_ maps to \x1F (Unit separator, Information separator one) + case ui::VKEY_OEM_MINUS: + return 0x1F; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + break; + } + } else { + switch (windows_key_code) { + // ctrl-[ maps to \x1B (Escape) + case ui::VKEY_OEM_4: + return 0x1B; + // ctrl-\ maps to \x1C (File separator, Information separator four) + case ui::VKEY_OEM_5: + return 0x1C; + // ctrl-] maps to \x1D (Group separator, Information separator three) + case ui::VKEY_OEM_6: + return 0x1D; + // ctrl-Enter maps to \x0A (Line feed) + case ui::VKEY_RETURN: + return 0x0A; + // Returns 0 for all other keys to avoid inputting unexpected chars. + default: + break; + } + } + return 0; +} +#endif #if defined(OS_WIN) WebKit::WebMouseEvent MakeUntranslatedWebMouseEventFromNativeEvent( base::NativeEvent native_event); @@ -21,28 +67,104 @@ WebKit::WebKeyboardEvent MakeWebKeyboardEventFromNativeEvent( WebKit::WebGestureEvent MakeWebGestureEventFromNativeEvent( base::NativeEvent native_event); #elif defined(USE_X11) -WebKit::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent( - ui::ScrollEvent* event); WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( ui::KeyEvent* event); -WebKit::WebGestureEvent MakeWebGestureEventFromAuraEvent( - ui::ScrollEvent* event); -#else +#elif defined(USE_OZONE) +WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( + ui::KeyEvent* event) { + base::NativeEvent native_event = event->native_event(); + ui::EventType type = ui::EventTypeFromNative(native_event); + WebKit::WebKeyboardEvent webkit_event; + + webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); + webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); + + switch (type) { + case ui::ET_KEY_PRESSED: + webkit_event.type = event->is_char() ? WebKit::WebInputEvent::Char : + WebKit::WebInputEvent::RawKeyDown; + break; + case ui::ET_KEY_RELEASED: + webkit_event.type = WebKit::WebInputEvent::KeyUp; + break; + default: + NOTREACHED(); + } + + if (webkit_event.modifiers & WebKit::WebInputEvent::AltKey) + webkit_event.isSystemKey = true; + + wchar_t character = ui::KeyboardCodeFromNative(native_event); + webkit_event.windowsKeyCode = character; + webkit_event.nativeKeyCode = character; + + if (webkit_event.windowsKeyCode == ui::VKEY_RETURN) + webkit_event.unmodifiedText[0] = '\r'; + else + webkit_event.unmodifiedText[0] = character; + + if (webkit_event.modifiers & WebKit::WebInputEvent::ControlKey) { + webkit_event.text[0] = + GetControlCharacter( + webkit_event.windowsKeyCode, + webkit_event.modifiers & WebKit::WebInputEvent::ShiftKey); + } else { + webkit_event.text[0] = webkit_event.unmodifiedText[0]; + } + + webkit_event.setKeyIdentifierFromWindowsKeyCode(); + + return webkit_event; +} +#endif +#if defined(USE_X11) || defined(USE_OZONE) WebKit::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent( ui::ScrollEvent* event) { WebKit::WebMouseWheelEvent webkit_event; - return webkit_event; -} -WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( - ui::KeyEvent* event) { - WebKit::WebKeyboardEvent webkit_event; + webkit_event.type = WebKit::WebInputEvent::MouseWheel; + webkit_event.button = WebKit::WebMouseEvent::ButtonNone; + webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); + webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); + webkit_event.hasPreciseScrollingDeltas = true; + webkit_event.deltaX = event->x_offset(); + if (event->x_offset_ordinal() != 0.f && event->x_offset() != 0.f) { + webkit_event.accelerationRatioX = + event->x_offset_ordinal() / event->x_offset(); + } + webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick; + webkit_event.deltaY = event->y_offset(); + webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick; + if (event->y_offset_ordinal() != 0.f && event->y_offset() != 0.f) { + webkit_event.accelerationRatioY = + event->y_offset_ordinal() / event->y_offset(); + } return webkit_event; } WebKit::WebGestureEvent MakeWebGestureEventFromAuraEvent( ui::ScrollEvent* event) { WebKit::WebGestureEvent webkit_event; + + switch (event->type()) { + case ui::ET_SCROLL_FLING_START: + webkit_event.type = WebKit::WebInputEvent::GestureFlingStart; + webkit_event.data.flingStart.velocityX = event->x_offset(); + webkit_event.data.flingStart.velocityY = event->y_offset(); + break; + case ui::ET_SCROLL_FLING_CANCEL: + webkit_event.type = WebKit::WebInputEvent::GestureFlingCancel; + break; + case ui::ET_SCROLL: + NOTREACHED() << "Invalid gesture type: " << event->type(); + break; + default: + NOTREACHED() << "Unknown gesture type: " << event->type(); + } + + webkit_event.sourceDevice = WebKit::WebGestureEvent::Touchpad; + webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); + webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); return webkit_event; } @@ -234,6 +356,7 @@ WebKit::WebMouseEvent MakeWebMouseEventFromAuraEvent(ui::MouseEvent* event) { break; case ui::ET_MOUSE_RELEASED: webkit_event.type = WebKit::WebInputEvent::MouseUp; + webkit_event.clickCount = event->GetClickCount(); break; case ui::ET_MOUSE_ENTERED: case ui::ET_MOUSE_EXITED: diff --git a/chromium/content/browser/renderer_host/web_input_event_aura.h b/chromium/content/browser/renderer_host/web_input_event_aura.h index ddc59e0c46f..3c4c8945bed 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aura.h +++ b/chromium/content/browser/renderer_host/web_input_event_aura.h @@ -22,6 +22,10 @@ namespace content { // Used for scrolling. This matches Firefox behavior. const int kPixelsPerTick = 53; +#if defined(USE_X11) || defined(USE_OZONE) +CONTENT_EXPORT WebKit::WebUChar GetControlCharacter( + int windows_key_code, bool shift); +#endif CONTENT_EXPORT WebKit::WebMouseEvent MakeWebMouseEvent( ui::MouseEvent* event); CONTENT_EXPORT WebKit::WebMouseWheelEvent MakeWebMouseWheelEvent( diff --git a/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc b/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc index 0443c30dad9..818250c0dd6 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc +++ b/chromium/content/browser/renderer_host/web_input_event_aura_unittest.cc @@ -6,11 +6,12 @@ #include "base/basictypes.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/events/event.h" +#include "ui/events/event.h" #if defined(USE_X11) #include <X11/keysym.h> #include <X11/Xlib.h> +#include "ui/gfx/x/x11_types.h" #include "ui/base/x/x11_util.h" #endif @@ -56,7 +57,7 @@ TEST(WebInputEventAuraTest, TestMakeWebKeyboardEventWindowsKeyCode) { ui::VKEY_CONTROL, 0, // X does not set ControlMask for KeyPress. &xev); - xev.xkey.keycode = XKeysymToKeycode(ui::GetXDisplay(), XK_Control_L); + xev.xkey.keycode = XKeysymToKeycode(gfx::GetXDisplay(), XK_Control_L); ui::KeyEvent event(&xev, false /* is_char */); WebKit::WebKeyboardEvent webkit_event = MakeWebKeyboardEvent(&event); // ui::VKEY_LCONTROL, instead of ui::VKEY_CONTROL, should be filled. @@ -68,7 +69,7 @@ TEST(WebInputEventAuraTest, TestMakeWebKeyboardEventWindowsKeyCode) { ui::VKEY_CONTROL, 0, // X does not set ControlMask for KeyPress. &xev); - xev.xkey.keycode = XKeysymToKeycode(ui::GetXDisplay(), XK_Control_R); + xev.xkey.keycode = XKeysymToKeycode(gfx::GetXDisplay(), XK_Control_R); ui::KeyEvent event(&xev, false /* is_char */); WebKit::WebKeyboardEvent webkit_event = MakeWebKeyboardEvent(&event); // ui::VKEY_RCONTROL, instead of ui::VKEY_CONTROL, should be filled. diff --git a/chromium/content/browser/renderer_host/web_input_event_aurax11.cc b/chromium/content/browser/renderer_host/web_input_event_aurax11.cc index fdde17edc7b..76ae55f5f19 100644 --- a/chromium/content/browser/renderer_host/web_input_event_aurax11.cc +++ b/chromium/content/browser/renderer_host/web_input_event_aurax11.cc @@ -45,10 +45,10 @@ #include "base/event_types.h" #include "base/logging.h" #include "content/browser/renderer_host/ui_events_helper.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_constants.h" -#include "ui/base/keycodes/keyboard_code_conversion_x.h" -#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace content { @@ -87,106 +87,8 @@ int XKeyEventToWindowsKeyCode(XKeyEvent* event) { return windows_key_code; } -// From third_party/WebKit/Source/web/gtk/WebInputEventFactory.cpp: -WebKit::WebUChar GetControlCharacter(int windows_key_code, bool shift) { - if (windows_key_code >= ui::VKEY_A && - windows_key_code <= ui::VKEY_Z) { - // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A - return windows_key_code - ui::VKEY_A + 1; - } - if (shift) { - // following graphics chars require shift key to input. - switch (windows_key_code) { - // ctrl-@ maps to \x00 (Null byte) - case ui::VKEY_2: - return 0; - // ctrl-^ maps to \x1E (Record separator, Information separator two) - case ui::VKEY_6: - return 0x1E; - // ctrl-_ maps to \x1F (Unit separator, Information separator one) - case ui::VKEY_OEM_MINUS: - return 0x1F; - // Returns 0 for all other keys to avoid inputting unexpected chars. - default: - break; - } - } else { - switch (windows_key_code) { - // ctrl-[ maps to \x1B (Escape) - case ui::VKEY_OEM_4: - return 0x1B; - // ctrl-\ maps to \x1C (File separator, Information separator four) - case ui::VKEY_OEM_5: - return 0x1C; - // ctrl-] maps to \x1D (Group separator, Information separator three) - case ui::VKEY_OEM_6: - return 0x1D; - // ctrl-Enter maps to \x0A (Line feed) - case ui::VKEY_RETURN: - return 0x0A; - // Returns 0 for all other keys to avoid inputting unexpected chars. - default: - break; - } - } - return 0; -} - } // namespace -WebKit::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent( - ui::ScrollEvent* event) { - WebKit::WebMouseWheelEvent webkit_event; - - webkit_event.type = WebKit::WebInputEvent::MouseWheel; - webkit_event.button = WebKit::WebMouseEvent::ButtonNone; - webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); - webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); - webkit_event.hasPreciseScrollingDeltas = true; - webkit_event.deltaX = event->x_offset(); - if (event->x_offset_ordinal() != 0.f && event->x_offset() != 0.f) { - webkit_event.accelerationRatioX = - event->x_offset_ordinal() / event->x_offset(); - } - webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick; - webkit_event.deltaY = event->y_offset(); - webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick; - if (event->y_offset_ordinal() != 0.f && event->y_offset() != 0.f) { - webkit_event.accelerationRatioY = - event->y_offset_ordinal() / event->y_offset(); - } - - return webkit_event; -} - -// NOTE: ui::ScrollEvent instances come from the touchpad. -WebKit::WebGestureEvent MakeWebGestureEventFromAuraEvent( - ui::ScrollEvent* event) { - WebKit::WebGestureEvent webkit_event; - - switch (event->type()) { - case ui::ET_SCROLL_FLING_START: - webkit_event.type = WebKit::WebInputEvent::GestureFlingStart; - webkit_event.data.flingStart.velocityX = event->x_offset(); - webkit_event.data.flingStart.velocityY = event->y_offset(); - break; - case ui::ET_SCROLL_FLING_CANCEL: - webkit_event.type = WebKit::WebInputEvent::GestureFlingCancel; - break; - case ui::ET_SCROLL: - NOTREACHED() << "Invalid gesture type: " << event->type(); - break; - default: - NOTREACHED() << "Unknown gesture type: " << event->type(); - } - - webkit_event.sourceDevice = WebKit::WebGestureEvent::Touchpad; - webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags()); - webkit_event.timeStampSeconds = event->time_stamp().InSecondsF(); - - return webkit_event; -} - WebKit::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent( ui::KeyEvent* event) { base::NativeEvent native_event = event->native_event(); diff --git a/chromium/content/browser/resources/indexed_db/indexeddb_internals.css b/chromium/content/browser/resources/indexed_db/indexeddb_internals.css index 76653f0921c..c52f5ff9754 100644 --- a/chromium/content/browser/resources/indexed_db/indexeddb_internals.css +++ b/chromium/content/browser/resources/indexed_db/indexeddb_internals.css @@ -48,22 +48,55 @@ font-weight: bold; } +.indexeddb-transaction-list { + margin-left: 10px; + border-collapse: collapse; +} + +.indexeddb-transaction-list th, +.indexeddb-transaction-list td { + padding: 2px 10px; + min-width: 50px; + max-width: 75px; +} + +td.indexeddb-transaction-scope { + min-width: 200px; + max-width: 500px; +} + +.indexeddb-transaction-list th { + background-color: rgb(249, 249, 249); + border: 1px solid rgb(156, 194, 239); + font-weight: normal; + text-align: left; +} + .indexeddb-transaction { background-color: rgb(235, 239, 249); - margin: 4px 0; - border-radius: 4px; - padding: 2px; - position: relative; + border-bottom: 2px solid white; } +.indexeddb-transaction.created { + font-weight: italic; +} +.indexeddb-transaction.started { + font-weight: bold; +} .indexeddb-transaction.running { font-weight: bold; } +.indexeddb-transaction.blocked { +} -.indexeddb-transaction-mode, -.indexeddb-transaction-scope, -.indexeddb-transaction-state { - margin: 0 8px; +.indexeddb-transaction.started .indexeddb-transaction-state { + background-color: rgb(249, 249, 235); +} +.indexeddb-transaction.running .indexeddb-transaction-state { + background-color: rgb(235, 249, 235); +} +.indexeddb-transaction.blocked .indexeddb-transaction-state { + background-color: rgb(249, 235, 235); } .controls a { diff --git a/chromium/content/browser/resources/indexed_db/indexeddb_internals.html b/chromium/content/browser/resources/indexed_db/indexeddb_internals.html index 153aef1aa11..781fcb0fcec 100644 --- a/chromium/content/browser/resources/indexed_db/indexeddb_internals.html +++ b/chromium/content/browser/resources/indexed_db/indexeddb_internals.html @@ -84,16 +84,85 @@ </span> </div> - <div class="indexeddb-transaction" - jsselect="$this.transactions" - jseval="this.classList.add($this.running ? 'running' : 'waiting')"> - <span>Transaction:</span> - <span class="indexeddb-transaction-mode" jscontent="mode"></span> - <span class="indexeddb-transaction-scope" jsdisplay="scope"> - <span>scope:</span> <span jscontent="'[ ' + scope.join(', ') + ' ]'"></span> - </span> - <span class="indexeddb-transaction-state" - jscontent="running ? '(running)' : '(blocked)'"></span> + <div jsdisplay="$this.transactions && + $this.transactions.length"> + <span>Transactions:</span> + + <table class="indexeddb-transaction-list"> + <tbody> + <tr> + <th title="Process ID of the tab or SharedWorker that created the transaction"> + Process ID + </th> + <th title="Transaction ID (unique within Process)"> + ID + </th> + <th title="Type of transaction"> + Mode + </th> + <th title="Names of object stores used by the transaction"> + Scope + </th> + <th title="Number of requests that have been executed"> + Completed Requests + </th> + <th title="Number of requests that have not yet been executed"> + Pending Requests + </th> + <th title="Time since transaction creation"> + Age (ms) + </th> + <th title="Time since transaction started"> + Runtime (ms) + </th> + <th title="Status in the transaction queue"> + Status + </th> + </tr> + <tr class="indexeddb-transaction" + jsselect="$this.transactions" + jseval="this.classList.add($this.status)"> + + <td class="indexeddb-transaction-pid" + jscontent="pid"> + </td> + + <td class="indexeddb-transaction-tid" + jscontent="tid"> + </td> + + <td class="indexeddb-transaction-mode" + jscontent="mode"> + </td> + + <td class="indexeddb-transaction-scope" + jscontent="'[ ' + scope.join(', ') + ' ]'"> + </td> + + <td class="indexeddb-transaction-requests-complete" + jscontent="tasks_completed"> + </td> + + <td class="indexeddb-transaction-requests-pending" + jscontent="tasks_scheduled - tasks_completed"> + </td> + + <td class="indexeddb-transaction-age" + jscontent="Math.round(age)"> + </td> + + <td class="indexeddb-transaction-age"> + <span jsdisplay="status == 'started' || status == 'running'" + jscontent="Math.round(runtime)"> + </span> + </td> + + <td class="indexeddb-transaction-state" + jscontent="status"> + </td> + </tr> + </tbody> + </table> </div> </div> </div> diff --git a/chromium/content/browser/resources/media/cache_entry.js b/chromium/content/browser/resources/media/cache_entry.js index 275a8c74a50..e4b3f0d3e05 100644 --- a/chromium/content/browser/resources/media/cache_entry.js +++ b/chromium/content/browser/resources/media/cache_entry.js @@ -176,6 +176,12 @@ cr.define('media', function() { * this file. */ generateDetails: function() { + function makeElement(tag, content) { + var toReturn = document.createElement(tag); + toReturn.textContent = content; + return toReturn; + } + this.details_.id = this.key; this.summaryText_.textContent = this.key || 'Unknown File'; @@ -188,8 +194,8 @@ cr.define('media', function() { this.detailTable_.appendChild(body); var headerRow = document.createElement('tr'); - headerRow.appendChild(media.makeElement('th', 'Read From Cache')); - headerRow.appendChild(media.makeElement('th', 'Written To Cache')); + headerRow.appendChild(makeElement('th', 'Read From Cache')); + headerRow.appendChild(makeElement('th', 'Written To Cache')); header.appendChild(headerRow); var footerRow = document.createElement('tr'); @@ -209,8 +215,8 @@ cr.define('media', function() { var length = Math.max(read.length, written.length); for (var i = 0; i < length; i++) { var row = document.createElement('tr'); - row.appendChild(media.makeElement('td', read[i] || '')); - row.appendChild(media.makeElement('td', written[i] || '')); + row.appendChild(makeElement('td', read[i] || '')); + row.appendChild(makeElement('td', written[i] || '')); body.appendChild(row); } diff --git a/chromium/content/browser/resources/media/client_renderer.js b/chromium/content/browser/resources/media/client_renderer.js new file mode 100644 index 00000000000..cac70e8b839 --- /dev/null +++ b/chromium/content/browser/resources/media/client_renderer.js @@ -0,0 +1,306 @@ +// Copyright 2013 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. + +var ClientRenderer = (function() { + var ClientRenderer = function() { + this.playerListElement = document.getElementById('player-list'); + this.audioStreamListElement = document.getElementById('audio-stream-list'); + this.propertiesTable = document.getElementById('property-table'); + this.logTable = document.getElementById('log'); + this.graphElement = document.getElementById('graphs'); + + this.selectedPlayer = null; + this.selectedStream = null; + + this.selectedPlayerLogIndex = 0; + + this.filterFunction = function() { return true; }; + this.filterText = document.getElementById('filter-text'); + this.filterText.onkeyup = this.onTextChange_.bind(this); + + this.bufferCanvas = document.createElement('canvas'); + this.bufferCanvas.width = media.BAR_WIDTH; + this.bufferCanvas.height = media.BAR_HEIGHT; + + this.clipboardTextarea = document.getElementById('clipboard-textarea'); + this.clipboardButton = document.getElementById('copy-button'); + this.clipboardButton.onclick = this.copyToClipboard_.bind(this); + }; + + function removeChildren(element) { + while (element.hasChildNodes()) { + element.removeChild(element.lastChild); + } + }; + + function createButton(text, select_cb) { + var button = document.createElement('button'); + + button.appendChild(document.createTextNode(text)); + button.onclick = function() { + select_cb(); + }; + + return button; + }; + + ClientRenderer.prototype = { + audioStreamAdded: function(audioStreams, audioStreamAdded) { + this.redrawAudioStreamList_(audioStreams); + }, + + audioStreamUpdated: function(audioStreams, stream, key, value) { + if (stream === this.selectedStream) { + this.drawProperties_(stream); + } + }, + + audioStreamRemoved: function(audioStreams, audioStreamRemoved) { + this.redrawAudioStreamList_(audioStreams); + }, + + /** + * Called when a player is added to the collection. + * @param players The entire map of id -> player. + * @param player_added The player that is added. + */ + playerAdded: function(players, playerAdded) { + this.redrawPlayerList_(players); + }, + + /** + * Called when a playre is removed from the collection. + * @param players The entire map of id -> player. + * @param player_added The player that was removed. + */ + playerRemoved: function(players, playerRemoved) { + this.redrawPlayerList_(players); + }, + + /** + * Called when a property on a player is changed. + * @param players The entire map of id -> player. + * @param player The player that had its property changed. + * @param key The name of the property that was changed. + * @param value The new value of the property. + */ + playerUpdated: function(players, player, key, value) { + if (player === this.selectedPlayer) { + this.drawProperties_(player.properties); + this.drawLog_(); + this.drawGraphs_(); + } + if (key === 'name' || key === 'url') { + this.redrawPlayerList_(players); + } + }, + + redrawAudioStreamList_: function(streams) { + removeChildren(this.audioStreamListElement); + + for (id in streams) { + var li = document.createElement('li'); + li.appendChild(createButton( + id, this.selectAudioStream_.bind(this, streams[id]))); + this.audioStreamListElement.appendChild(li); + } + }, + + selectAudioStream_: function(audioStream) { + this.selectedStream = audioStream; + this.selectedPlayer = null; + this.drawProperties_(audioStream); + removeChildren(this.logTable.querySelector('tbody')); + removeChildren(this.graphElement); + }, + + redrawPlayerList_: function(players) { + removeChildren(this.playerListElement); + + for (id in players) { + var li = document.createElement('li'); + var player = players[id]; + var usableName = player.properties.name || + player.properties.url || + 'player ' + player.id; + + li.appendChild(createButton( + usableName, this.selectPlayer_.bind(this, player))); + this.playerListElement.appendChild(li); + } + }, + + selectPlayer_: function(player) { + this.selectedPlayer = player; + this.selectedPlayerLogIndex = 0; + this.selectedStream = null; + this.drawProperties_(player.properties); + + removeChildren(this.logTable.querySelector('tbody')); + removeChildren(this.graphElement); + this.drawLog_(); + this.drawGraphs_(); + }, + + drawProperties_: function(propertyMap) { + removeChildren(this.propertiesTable); + + for (key in propertyMap) { + var value = propertyMap[key]; + + var row = this.propertiesTable.insertRow(-1); + var keyCell = row.insertCell(-1); + var valueCell = row.insertCell(-1); + + keyCell.appendChild(document.createTextNode(key)); + valueCell.appendChild(document.createTextNode(value)); + } + }, + + appendEventToLog_: function(event) { + if (this.filterFunction(event.key)) { + var row = this.logTable.querySelector('tbody').insertRow(-1); + + row.insertCell(-1).appendChild(document.createTextNode( + util.millisecondsToString(event.time))); + row.insertCell(-1).appendChild(document.createTextNode(event.key)); + row.insertCell(-1).appendChild(document.createTextNode(event.value)); + } + }, + + drawLog_: function() { + var toDraw = this.selectedPlayer.allEvents.slice( + this.selectedPlayerLogIndex); + toDraw.forEach(this.appendEventToLog_.bind(this)); + this.selectedPlayerLogIndex = this.selectedPlayer.allEvents.length; + }, + + drawGraphs_: function() { + function addToGraphs(name, graph, graphElement) { + var li = document.createElement('li'); + li.appendChild(graph); + li.appendChild(document.createTextNode(name)); + graphElement.appendChild(li); + } + + var url = this.selectedPlayer.properties.url; + if (!url) { + return; + } + + var cache = media.cacheForUrl(url); + + var player = this.selectedPlayer; + var props = player.properties; + + var cacheExists = false; + var bufferExists = false; + + if (props['buffer_start'] !== undefined && + props['buffer_current'] !== undefined && + props['buffer_end'] !== undefined && + props['total_bytes'] !== undefined) { + this.drawBufferGraph_(props['buffer_start'], + props['buffer_current'], + props['buffer_end'], + props['total_bytes']); + bufferExists = true; + } + + if (cache) { + if (player.properties['total_bytes']) { + cache.size = Number(player.properties['total_bytes']); + } + cache.generateDetails(); + cacheExists = true; + + } + + if (!this.graphElement.hasChildNodes()) { + if (bufferExists) { + addToGraphs('buffer', this.bufferCanvas, this.graphElement); + } + if (cacheExists) { + addToGraphs('cache read', cache.readCanvas, this.graphElement); + addToGraphs('cache write', cache.writeCanvas, this.graphElement); + } + } + }, + + drawBufferGraph_: function(start, current, end, size) { + var ctx = this.bufferCanvas.getContext('2d'); + var width = this.bufferCanvas.width; + var height = this.bufferCanvas.height; + ctx.fillStyle = '#aaa'; + ctx.fillRect(0, 0, width, height); + + var scale_factor = width / size; + var left = start * scale_factor; + var middle = current * scale_factor; + var right = end * scale_factor; + + ctx.fillStyle = '#a0a'; + ctx.fillRect(left, 0, middle - left, height); + ctx.fillStyle = '#aa0'; + ctx.fillRect(middle, 0, right - middle, height); + }, + + copyToClipboard_: function() { + var properties = this.selectedStream || + this.selectedPlayer.properties || false; + if (!properties) { + return; + } + var stringBuffer = []; + + for (var key in properties) { + var value = properties[key]; + stringBuffer.push(key.toString()); + stringBuffer.push(': '); + stringBuffer.push(value.toString()); + stringBuffer.push('\n'); + } + + this.clipboardTextarea.value = stringBuffer.join(''); + this.clipboardTextarea.classList.remove('hidden'); + this.clipboardTextarea.focus(); + this.clipboardTextarea.select(); + + // The act of copying anything from the textarea gets canceled + // if the element in question gets the class 'hidden' (which contains the + // css property display:none) before the event is finished. For this, it + // is necessary put the property setting on the event loop to be executed + // after the copy has taken place. + this.clipboardTextarea.oncopy = function(event) { + setTimeout(function(element) { + event.target.classList.add('hidden'); + }, 0); + }; + }, + + onTextChange_: function(event) { + var text = this.filterText.value.toLowerCase(); + var parts = text.split(',').map(function(part) { + return part.trim(); + }).filter(function(part) { + return part.trim().length > 0; + }); + + this.filterFunction = function(text) { + text = text.toLowerCase(); + return parts.length === 0 || parts.some(function(part) { + return text.indexOf(part) != -1; + }); + }; + + if (this.selectedPlayer) { + removeChildren(this.logTable.querySelector('tbody')); + this.selectedPlayerLogIndex = 0; + this.drawLog_(); + } + }, + }; + + return ClientRenderer; +})(); diff --git a/chromium/content/browser/resources/media/event_list.js b/chromium/content/browser/resources/media/event_list.js deleted file mode 100644 index df4d4273063..00000000000 --- a/chromium/content/browser/resources/media/event_list.js +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2012 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. - -cr.define('media', function() { - 'use strict'; - - /** - * This class holds a list of MediaLogEvents. - * It inherits from <li> and contains a tabular list of said events, - * the time at which they occurred, and their parameters. - */ - var EventList = cr.ui.define('li'); - - EventList.prototype = { - __proto__: HTMLLIElement.prototype, - startTime_: null, - - /** - * Decorate this list item as an EventList. - */ - decorate: function() { - this.table_ = document.createElement('table'); - var details = document.createElement('details'); - var summary = media.makeElement('summary', 'Log:'); - details.appendChild(summary); - details.appendChild(this.table_); - this.appendChild(details); - - var hRow = document.createElement('tr'); - hRow.appendChild(media.makeElement('th', 'Time:')); - hRow.appendChild(media.makeElement('th', 'Event:')); - hRow.appendChild(media.makeElement('th', 'Parameters:')); - var header = document.createElement('thead'); - header.appendChild(hRow); - this.table_.appendChild(header); - }, - - /** - * Add an event to the list. It is stored as a new row in this.table_. - * @param {Object} event The MediaLogEvent that has occurred. - */ - addEvent: function(event) { - this.startTime_ = this.startTime_ || event.ticksMillis; - var normalizedTicksMillis = event.ticksMillis - this.startTime_; - - var row = document.createElement('tr'); - row.appendChild(media.makeElement( - 'td', normalizedTicksMillis.toFixed(1))); - row.appendChild(media.makeElement('td', event.type)); - var params = []; - for (var key in event.params) { - params.push(key + ': ' + event.params[key]); - } - - row.appendChild(media.makeElement('td', params.join(', '))); - this.table_.appendChild(row); - } - }; - - return { - EventList: EventList - }; -}); diff --git a/chromium/content/browser/resources/media/item_store.js b/chromium/content/browser/resources/media/item_store.js deleted file mode 100644 index a6e3a6c4577..00000000000 --- a/chromium/content/browser/resources/media/item_store.js +++ /dev/null @@ -1,70 +0,0 @@ -// 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. - -cr.define('media', function() { - - /** - * This class stores hashes by their id field and provides basic methods for - * iterating over the collection. - * @constructor - */ - function ItemStore() { - this.items_ = {}; - } - - ItemStore.prototype = { - /** - * Get a sorted list of item ids. - * @return {Array} A sorted array of ids. - */ - ids: function() { - var ids = []; - for (var i in this.items_) - ids.push(i); - return ids.sort(); - }, - - /** - * Add an item to the store. - * @param {Object} item The item to be added. - * @param {string} item.id The id of the item. - */ - addItem: function(item) { - this.items_[item.id] = item; - }, - - /** - * Add a dictionary of items to the store. - * @param {Object} items A dictionary of individual items. The keys are - * irrelevant but each must have an id field. - */ - addItems: function(items) { - for (id in items) - this.addItem(items[id]); - }, - - /** - * Remove an item from the store. - * @param {string} id The id of the item to be removed. - */ - removeItem: function(id) { - delete this.items_[id]; - }, - - /** - * Map this itemStore to an Array. Items are sorted by id. - * @param {function(*)} mapper The mapping function applied to each item. - * @return {Array} An array of mapped items. - */ - map: function(mapper) { - var items = this.items_; - var ids = this.ids(); - return ids.map(function(id) { return mapper(items[id]); }); - } - }; - - return { - ItemStore: ItemStore - }; -}); diff --git a/chromium/content/browser/resources/media/main.js b/chromium/content/browser/resources/media/main.js new file mode 100644 index 00000000000..14b9918a2de --- /dev/null +++ b/chromium/content/browser/resources/media/main.js @@ -0,0 +1,195 @@ +// Copyright 2013 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. + +/** + * A global object that gets used by the C++ interface. + */ +var media = (function() { + 'use strict'; + + var manager = null; + + // A number->string mapping that is populated through the backend that + // describes the phase that the network entity is in. + var eventPhases = {}; + + // A number->string mapping that is populated through the backend that + // describes the type of event sent from the network. + var eventTypes = {}; + + // A mapping of number->CacheEntry where the number is a unique id for that + // network request. + var cacheEntries = {}; + + // A mapping of url->CacheEntity where the url is the url of the resource. + var cacheEntriesByKey = {}; + + var requrestURLs = {}; + + var media = { + BAR_WIDTH: 200, + BAR_HEIGHT: 25 + }; + + /** + * Users of |media| must call initialize prior to calling other methods. + */ + media.initialize = function(theManager) { + manager = theManager; + }; + + media.onReceiveEverything = function(everything) { + for (var key in everything.audio_streams) { + media.updateAudioStream(everything.audio_streams[key]); + } + }; + + media.onReceiveConstants = function(constants) { + for (var key in constants.eventTypes) { + var value = constants.eventTypes[key]; + eventTypes[value] = key; + } + + for (var key in constants.eventPhases) { + var value = constants.eventPhases[key]; + eventPhases[value] = key; + } + }; + + media.cacheForUrl = function(url) { + return cacheEntriesByKey[url]; + }; + + media.onNetUpdate = function(updates) { + updates.forEach(function(update) { + var id = update.source.id; + if (!cacheEntries[id]) + cacheEntries[id] = new media.CacheEntry; + + switch (eventPhases[update.phase] + '.' + eventTypes[update.type]) { + case 'PHASE_BEGIN.DISK_CACHE_ENTRY_IMPL': + var key = update.params.key; + + // Merge this source with anything we already know about this key. + if (cacheEntriesByKey[key]) { + cacheEntriesByKey[key].merge(cacheEntries[id]); + cacheEntries[id] = cacheEntriesByKey[key]; + } else { + cacheEntriesByKey[key] = cacheEntries[id]; + } + cacheEntriesByKey[key].key = key; + break; + + case 'PHASE_BEGIN.SPARSE_READ': + cacheEntries[id].readBytes(update.params.offset, + update.params.buff_len); + cacheEntries[id].sparse = true; + break; + + case 'PHASE_BEGIN.SPARSE_WRITE': + cacheEntries[id].writeBytes(update.params.offset, + update.params.buff_len); + cacheEntries[id].sparse = true; + break; + + case 'PHASE_BEGIN.URL_REQUEST_START_JOB': + requrestURLs[update.source.id] = update.params.url; + break; + + case 'PHASE_NONE.HTTP_TRANSACTION_READ_RESPONSE_HEADERS': + // Record the total size of the file if this was a range request. + var range = /content-range:\s*bytes\s*\d+-\d+\/(\d+)/i.exec( + update.params.headers); + var key = requrestURLs[update.source.id]; + delete requrestURLs[update.source.id]; + if (range && key) { + if (!cacheEntriesByKey[key]) { + cacheEntriesByKey[key] = new media.CacheEntry; + cacheEntriesByKey[key].key = key; + } + cacheEntriesByKey[key].size = range[1]; + } + break; + } + }); + }; + + media.onRendererTerminated = function(renderId) { + util.object.forEach(manager.players_, function(playerInfo, id) { + if (playerInfo.properties['render_id'] == renderId) { + manager.removePlayer(id); + } + }); + }; + + // For whatever reason, addAudioStream is also called on + // the removal of audio streams. + media.addAudioStream = function(event) { + switch (event.status) { + case 'created': + manager.addAudioStream(event.id); + manager.updateAudioStream(event.id, { 'playing': event.playing }); + break; + case 'closed': + manager.removeAudioStream(event.id); + break; + } + }; + + media.updateAudioStream = function(stream) { + manager.addAudioStream(stream.id); + manager.updateAudioStream(stream.id, stream); + }; + + media.onItemDeleted = function() { + // This only gets called when an audio stream is removed, which + // for whatever reason is also handled by addAudioStream... + // Because it is already handled, we can safely ignore it. + }; + + media.onPlayerOpen = function(id, timestamp) { + manager.addPlayer(id, timestamp); + }; + + media.onMediaEvent = function(event) { + var source = event.renderer + ':' + event.player; + + // Although this gets called on every event, there is nothing we can do + // because there is no onOpen event. + media.onPlayerOpen(source); + manager.updatePlayerInfoNoRecord( + source, event.ticksMillis, 'render_id', event.renderer); + manager.updatePlayerInfoNoRecord( + source, event.ticksMillis, 'player_id', event.player); + + var propertyCount = 0; + util.object.forEach(event.params, function(value, key) { + key = key.trim(); + + // These keys get spammed *a lot*, so put them on the display + // but don't log list. + if (key === 'buffer_start' || + key === 'buffer_end' || + key === 'buffer_current' || + key === 'is_downloading_data') { + manager.updatePlayerInfoNoRecord( + source, event.ticksMillis, key, value); + } else { + manager.updatePlayerInfo(source, event.ticksMillis, key, value); + } + propertyCount += 1; + }); + + if (propertyCount === 0) { + manager.updatePlayerInfo( + source, event.ticksMillis, 'EVENT', event.type); + } + }; + + // |chrome| is not defined during tests. + if (window.chrome && window.chrome.send) { + chrome.send('getEverything'); + } + return media; +}()); diff --git a/chromium/content/browser/resources/media/manager.js b/chromium/content/browser/resources/media/manager.js new file mode 100644 index 00000000000..1ba130181e5 --- /dev/null +++ b/chromium/content/browser/resources/media/manager.js @@ -0,0 +1,114 @@ +// Copyright 2013 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. + +/** + * @fileoverview Keeps track of all the existing PlayerInfo and + * audio stream objects and is the entry-point for messages from the backend. + * + * The events captured by Manager (add, remove, update) are relayed + * to the clientRenderer which it can choose to use to modify the UI. + */ +var Manager = (function() { + 'use strict'; + + function Manager(clientRenderer) { + this.players_ = {}; + this.audioStreams_ = {}; + this.clientRenderer_ = clientRenderer; + } + + Manager.prototype = { + /** + * Adds an audio-stream to the dictionary of audio-streams to manage. + * @param id The unique-id of the audio-stream. + */ + addAudioStream: function(id) { + this.audioStreams_[id] = this.audioStreams_[id] || {}; + this.clientRenderer_.audioStreamAdded(this.audioStreams_, + this.audioStreams_[id]); + }, + + /** + * Sets properties of an audiostream. + * @param id The unique-id of the audio-stream. + * @param properties A dictionary of properties to be added to the + * audio-stream. + */ + updateAudioStream: function(id, properties) { + for (var key in properties) { + this.audioStreams_[id][key] = properties[key]; + } + this.clientRenderer_.audioStreamAdded( + this.audioStreams_, this.audioStreams_[id]); + }, + + /** + * Removes an audio-stream from the manager. + * @param id The unique-id of the audio-stream. + */ + removeAudioStream: function(id) { + this.clientRenderer_.audioStreamRemoved( + this.audioStreams_, this.audioStreams_[id]); + delete this.audioStreams_[id]; + }, + + + /** + * Adds a player to the list of players to manage. + */ + addPlayer: function(id) { + if (this.players_[id]) { + return; + } + // Make the PlayerProperty and add it to the mapping + this.players_[id] = new PlayerInfo(id); + this.clientRenderer_.playerAdded(this.players_, this.players_[id]); + }, + + /** + * Attempts to remove a player from the UI. + * @param id The ID of the player to remove. + */ + removePlayer: function(id) { + delete this.players_[id]; + this.clientRenderer_.playerRemoved(this.players_, this.players_[id]); + }, + + updatePlayerInfoNoRecord: function(id, timestamp, key, value) { + if (!this.players_[id]) { + console.error('[updatePlayerInfo] Id ' + id + ' does not exist'); + return; + } + + this.players_[id].addPropertyNoRecord(timestamp, key, value); + this.clientRenderer_.playerUpdated(this.players_, + this.players_[id], + key, + value); + }, + + /** + * + * @param id The unique ID that identifies the player to be updated. + * @param timestamp The timestamp of when the change occured. This + * timestamp is *not* normalized. + * @param key The name of the property to be added/changed. + * @param value The value of the property. + */ + updatePlayerInfo: function(id, timestamp, key, value) { + if (!this.players_[id]) { + console.error('[updatePlayerInfo] Id ' + id + ' does not exist'); + return; + } + + this.players_[id].addProperty(timestamp, key, value); + this.clientRenderer_.playerUpdated(this.players_, + this.players_[id], + key, + value); + } + }; + + return Manager; +}()); diff --git a/chromium/content/browser/resources/media/media_internals.css b/chromium/content/browser/resources/media/media_internals.css index d83b6b71eeb..9e6ab7608f2 100644 --- a/chromium/content/browser/resources/media/media_internals.css +++ b/chromium/content/browser/resources/media/media_internals.css @@ -1,83 +1,108 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. +/* Copyright 2013 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. */ -body { - font-family: sans-serif; -} +html, +body, +#container { + margin: 0; + padding: 0; + width: 100%; + height: 100%; -h2 { - margin: 15px 0 5px 0; + font-family: 'Lucida Grande', sans-serif; } -ul, -p, -canvas { - margin: 0; +table { + border-collapse: collapse; } - -[hidden] { - display: none !important; +td { + border: 1px solid black; + word-wrap: break-word; + max-width: 200px; } - -#media-players td, -#media-players th { - padding: 0 10px; +thead { + color: rgb(50,50,50); + font-size: 1.1em; } -.audio-stream[status='created'] { - color: blue; +h1, +h2, +h3 { + color: rgb(50,50,50); } -.audio-stream[status='closed'] { - text-decoration: line-through; +#container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-items: flex-start; + align-content: stretch; } -.audio-stream[status='error'] { - color: red; +#container > * { + padding: 0; + padding-left: 25px; + margin: 0; } -#cache-entries ul, -#media-players ul, -#media-players { - list-style-type: none; +#list-wrapper { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + align-content: stretch; } -.cache-entry { - margin: 0 0 5px 0; +#player-list-wrapper, +#audio-stream-list-wrapper { + flex-grow: 1; + align-self: stretch; + min-width: 200px; + max-width: 200px; + overflow: auto; } -.cache-entry-controls { - font-size: smaller; +#player-list-wrapper ul, +#player-list-wrapper li, +#audio-stream-list-wrapper ul, +#audio-stream-list-wrapper li { + margin: 0; + padding: 0; + list-style-type: none; } - -.cache-table { - table-layout: fixed; - width: 500px; +#list-wrapper button { + margin: 0; + padding: 0; + width: 170px; } -thead { - text-align: left; +#property-wrapper, +#log-wrapper { + display:block; + flex-grow: 0.5; + align-self: stretch; + overflow: auto; } -tfoot { - text-align: right; +#log-wrapper > thead { + position: fixed; } -.buffered { - display: table; +#graphs li { + list-style-type: none; } -.buffered > div { - display: table-row; -} +#clipboard-textarea { + position: absolute; + width: 50%; + height: 50%; -.buffered > div > div { - display: table-cell; - vertical-align: bottom; + left: 25%; + top: 25%; } -.buffered > div > div:first-child { - font-weight: bold; - padding-right: 2px; +.hidden { + display: none; } diff --git a/chromium/content/browser/resources/media/media_internals.html b/chromium/content/browser/resources/media/media_internals.html index 05d321f0ac7..6bfc48198d4 100644 --- a/chromium/content/browser/resources/media/media_internals.html +++ b/chromium/content/browser/resources/media/media_internals.html @@ -1,28 +1,57 @@ -<!DOCTYPE HTML> -<html i18n-values="dir:textdirection;"> <!-- -Copyright (c) 2012 The Chromium Authors. All rights reserved. +Copyright 2013 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. --> - <head> - <link rel="stylesheet" href="media_internals.css"> - <script src="chrome://resources/js/cr.js"></script> - <script src="chrome://resources/js/cr/ui.js"></script> - <script src="chrome://resources/js/util.js"></script> - <script src="chrome://media-internals/media_internals.js"></script> - <script src="chrome://media-internals/strings.js"></script> - <title>Media Internals</title> - </head> - <body> - <h2>Active media players:</h2> - <ul id="media-players"></ul> - <h2>Active audio streams:</h2> - <div id="audio-streams"></div> - <h2>Cached resources:</h2> - <div id="cache-entries"></div> - <script src="chrome://resources/js/i18n_template.js"></script> - <script src="chrome://resources/js/i18n_process.js"></script> - <script src="chrome://resources/js/jstemplate_compiled.js"></script> - </body> +<!DOCTYPE html> +<html i18n-values="dir:textdirection"> +<head> + <meta charset="utf-8"> + <title i18n-content="Media Internals"></title> + <link rel="stylesheet" href="media_internals.css"> + <script src="chrome://resources/js/cr.js"></script> +</head> + +<body> + <textarea id="clipboard-textarea" class="hidden"></textarea> + <div id="container"> + <div id="list-wrapper"> + <div id="player-list-wrapper"> + <h2>Players</h2> + <ul id="player-list"></ul> + </div> + <div id="audio-stream-list-wrapper"> + <h2>Audio Streams</h2> + <ul id="audio-stream-list"></ul> + </div> + </div> + <div id="property-wrapper"> + <h2>Properties <button id="copy-button">copy to clipboard</button> </h2> + <table id="property-table"> + <thead> + <tr> + <td>Property Name</td> <td>Value</td> + </tr> + </thead> + </table> + <ul id="graphs"></ul> + </div> + <div id="log-wrapper"> + <h2> + Log <input id="filter-text" type="text"> + </h2> + <table id="log"> + <thead> + <tr> + <td>Timestamp</td> + <td>Property</td> + <td>Value</td> + </tr> + </thead> + <tbody></tbody> + </table> + </div> + </div> + <script src="chrome://media-internals/media_internals.js"></script> +</body> </html> diff --git a/chromium/content/browser/resources/media/media_internals.js b/chromium/content/browser/resources/media/media_internals.js index c3e3a1ed1d1..936f86d357b 100644 --- a/chromium/content/browser/resources/media/media_internals.js +++ b/chromium/content/browser/resources/media/media_internals.js @@ -1,281 +1,15 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 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. +var media = {}; + +<include src="main.js"/> +<include src="util.js"/> <include src="cache_entry.js"/> <include src="disjoint_range_set.js"/> -<include src="event_list.js"/> -<include src="item_store.js"/> -<include src="media_player.js"/> -<include src="metrics.js"/> -<include src="util.js"/> - -cr.define('media', function() { - 'use strict'; - - // Stores information on open audio streams, referenced by id. - var audioStreams = new media.ItemStore; - - // Active media players, indexed by 'render_id:player_id'. - var mediaPlayers = {}; - - // Cached files indexed by key and source id. - var cacheEntriesByKey = {}; - var cacheEntries = {}; - - // Map of event source -> url. - var requestURLs = {}; - - // Constants passed to us from Chrome. - var eventTypes = {}; - var eventPhases = {}; - - // The <div>s on the page in which to display information. - var audioStreamDiv; - var cacheDiv; - - // A timer used to limit the rate of redrawing the Media Players section. - var redrawTimer = null; - - /** - * Initialize variables and ask MediaInternals for all its data. - */ - function initialize() { - audioStreamDiv = $('audio-streams'); - cacheDiv = $('cache-entries'); - - // Get information about all currently active media. - chrome.send('getEverything'); - } - - /** - * Write the set of audio streams to the DOM. - */ - function printAudioStreams() { - - /** - * Render a single stream as a <li>. - * @param {Object} stream The stream to render. - * @return {HTMLElement} A <li> containing the stream information. - */ - function printStream(stream) { - var out = document.createElement('li'); - out.id = stream.id; - out.className = 'audio-stream'; - out.setAttribute('status', stream.status); - - out.textContent += 'Audio stream ' + stream.id.split('.')[1]; - out.textContent += ' is ' + (stream.playing ? 'playing' : 'paused'); - if (typeof stream.volume != 'undefined') { - out.textContent += ' at ' + (stream.volume * 100).toFixed(0); - out.textContent += '% volume.'; - } - return out; - } - - var out = document.createElement('ul'); - audioStreams.map(printStream).forEach(function(s) { - out.appendChild(s); - }); - - audioStreamDiv.textContent = ''; - audioStreamDiv.appendChild(out); - } - - /** - * Redraw each MediaPlayer. - */ - function printMediaPlayers() { - for (var key in mediaPlayers) { - mediaPlayers[key].redraw(); - } - redrawTimer = null; - } - - /** - * Write the set of sparse CacheEntries to the DOM. - */ - function printSparseCacheEntries() { - var out = document.createElement('ul'); - for (var key in cacheEntriesByKey) { - if (cacheEntriesByKey[key].sparse) - out.appendChild(cacheEntriesByKey[key].toListItem()); - } - - cacheDiv.textContent = ''; - cacheDiv.appendChild(out); - } - - /** - * Receiving data for an audio stream. - * Add it to audioStreams and update the page. - * @param {Object} stream JSON representation of an audio stream. - */ - function addAudioStream(stream) { - audioStreams.addItem(stream); - printAudioStreams(); - } - - /** - * Receiving all data. - * Add it all to the appropriate stores and update the page. - * @param {Object} stuff JSON containing lists of data. - * @param {Object} stuff.audio_streams A dictionary of audio streams. - */ - function onReceiveEverything(stuff) { - audioStreams.addItems(stuff.audio_streams); - printAudioStreams(); - } - - /** - * Removing an item from the appropriate store. - * @param {string} id The id of the item to be removed, in the format - * "item_type.identifying_info". - */ - function onItemDeleted(id) { - var type = id.split('.')[0]; - switch (type) { - case 'audio_streams': - audioStreams.removeItem(id); - printAudioStreams(); - break; - } - } - - /** - * A render process has ended, delete any media players associated with it. - * @param {number} renderer The id of the render process. - */ - function onRendererTerminated(renderer) { - for (var key in mediaPlayers) { - if (mediaPlayers[key].renderer == renderer) { - $('media-players').removeChild(mediaPlayers[key]); - delete mediaPlayers[key]; - break; - } - } - printMediaPlayers(); - } - - /** - * Receiving net events. - * Update cache information and update that section of the page. - * @param {Array} updates A list of net events that have occurred. - */ - function onNetUpdate(updates) { - updates.forEach(function(update) { - var id = update.source.id; - if (!cacheEntries[id]) - cacheEntries[id] = new media.CacheEntry; - - switch (eventPhases[update.phase] + '.' + eventTypes[update.type]) { - case 'PHASE_BEGIN.DISK_CACHE_ENTRY_IMPL': - var key = update.params.key; - - // Merge this source with anything we already know about this key. - if (cacheEntriesByKey[key]) { - cacheEntriesByKey[key].merge(cacheEntries[id]); - cacheEntries[id] = cacheEntriesByKey[key]; - } else { - cacheEntriesByKey[key] = cacheEntries[id]; - } - cacheEntriesByKey[key].key = key; - break; - - case 'PHASE_BEGIN.SPARSE_READ': - cacheEntries[id].readBytes(update.params.offset, - update.params.buff_len); - cacheEntries[id].sparse = true; - break; - - case 'PHASE_BEGIN.SPARSE_WRITE': - cacheEntries[id].writeBytes(update.params.offset, - update.params.buff_len); - cacheEntries[id].sparse = true; - break; - - case 'PHASE_BEGIN.URL_REQUEST_START_JOB': - requestURLs[update.source.id] = update.params.url; - break; - - case 'PHASE_NONE.HTTP_TRANSACTION_READ_RESPONSE_HEADERS': - // Record the total size of the file if this was a range request. - var range = /content-range:\s*bytes\s*\d+-\d+\/(\d+)/i.exec( - update.params.headers); - var key = requestURLs[update.source.id]; - delete requestURLs[update.source.id]; - if (range && key) { - if (!cacheEntriesByKey[key]) { - cacheEntriesByKey[key] = new media.CacheEntry; - cacheEntriesByKey[key].key = key; - } - cacheEntriesByKey[key].size = range[1]; - } - break; - } - }); - - printSparseCacheEntries(); - } - - /** - * Receiving values for constants. Store them for later use. - * @param {Object} constants A dictionary of constants. - * @param {Object} constants.eventTypes A dictionary of event name -> int. - * @param {Object} constants.eventPhases A dictionary of event phase -> int. - */ - function onReceiveConstants(constants) { - var events = constants.eventTypes; - for (var e in events) { - eventTypes[events[e]] = e; - } - - var phases = constants.eventPhases; - for (var p in phases) { - eventPhases[phases[p]] = p; - } - } - - /** - * Receiving notification of a media event. - * @param {Object} event The json representation of a MediaLogEvent. - */ - function onMediaEvent(event) { - var source = event.renderer + ':' + event.player; - var item = mediaPlayers[source] || - new media.MediaPlayer({id: source, renderer: event.renderer}); - mediaPlayers[source] = item; - item.addEvent(event); - - // Both media and net events could provide the size of the file. - // Media takes priority, but keep the size in both places synchronized. - if (cacheEntriesByKey[item.properties.url]) { - item.properties.total_bytes = item.properties.total_bytes || - cacheEntriesByKey[item.properties.url].size; - cacheEntriesByKey[item.properties.url].size = item.properties.total_bytes; - } - - // Events tend to arrive in groups; don't redraw the page too often. - if (!redrawTimer) - redrawTimer = setTimeout(printMediaPlayers, 50); - } - - return { - initialize: initialize, - addAudioStream: addAudioStream, - cacheEntriesByKey: cacheEntriesByKey, - onReceiveEverything: onReceiveEverything, - onItemDeleted: onItemDeleted, - onRendererTerminated: onRendererTerminated, - onNetUpdate: onNetUpdate, - onReceiveConstants: onReceiveConstants, - onMediaEvent: onMediaEvent - }; -}); +<include src="player_info.js"/> +<include src="manager.js"/> +<include src="client_renderer.js"/> -/** - * Initialize everything once we have access to the DOM. - */ -document.addEventListener('DOMContentLoaded', function() { - media.initialize(); -}); +media.initialize(new Manager(new ClientRenderer())); diff --git a/chromium/content/browser/resources/media/media_player.js b/chromium/content/browser/resources/media/media_player.js deleted file mode 100644 index a9d5d6b3333..00000000000 --- a/chromium/content/browser/resources/media/media_player.js +++ /dev/null @@ -1,154 +0,0 @@ -// 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. - -cr.define('media', function() { - 'use strict'; - - /** - * This class inherits from <li> and is designed to store and display - * information about an open media player. - */ - var MediaPlayer = cr.ui.define('li'); - - MediaPlayer.prototype = { - __proto__: HTMLLIElement.prototype, - renderer: null, - id: null, - - /** - * Decorate this <li> as a MediaPlayer. - */ - decorate: function() { - this.properties = {}; - - this.url_ = document.createElement('span'); - this.url_.textContent = 'URL Unknown'; - - this.summary_ = document.createElement('summary'); - this.summary_.appendChild(this.url_); - - var bufferedDiv = document.createElement('div'); - bufferedDiv.className = 'buffered'; - this.summary_.appendChild(bufferedDiv); - - // Create our canvii. - function createCanvas(label) { - var canvas = document.createElement('canvas'); - canvas.width = media.BAR_WIDTH; - canvas.height = media.BAR_HEIGHT; - return canvas; - } - this.bufferedCanvas_ = createCanvas(); - this.cacheReadsCanvas_ = createCanvas(); - this.cacheWritesCanvas_ = createCanvas(); - - // Create our per-canvas entry divs that are initially hidden. - function addEntry(label, canvas) { - var labelDiv = document.createElement('div'); - labelDiv.textContent = label; - var canvasDiv = document.createElement('div'); - canvasDiv.appendChild(canvas); - var entryDiv = document.createElement('div'); - entryDiv.appendChild(labelDiv); - entryDiv.appendChild(canvasDiv); - entryDiv.hidden = true; - bufferedDiv.appendChild(entryDiv); - return entryDiv; - } - this.bufferedEntry_ = addEntry('Buffered', this.bufferedCanvas_); - this.cacheReadsEntry_ = addEntry('Cache Reads', this.cacheReadsCanvas_); - this.cacheWritesEntry_ = addEntry( - 'Cache Writes', this.cacheWritesCanvas_); - - this.details_ = document.createElement('details'); - this.details_.appendChild(this.summary_); - - this.propertyTable_ = document.createElement('table'); - this.events_ = new media.EventList; - this.metrics_ = new media.Metrics; - - var properties = media.createDetailsLi(); - properties.summary.textContent = 'Properties:'; - properties.details.appendChild(this.propertyTable_); - - var ul = document.createElement('ul'); - ul.appendChild(properties); - ul.appendChild(this.metrics_); - ul.appendChild(this.events_); - this.details_.appendChild(ul); - - this.appendChild(this.details_); - $('media-players').appendChild(this); - }, - - /** - * Record an event and update statistics etc. - * @param {Object} event The event that occurred. - */ - addEvent: function(event) { - for (var key in event.params) { - this.properties[key] = event.params[key]; - } - - if (event.type == 'LOAD' && event.params['url']) { - this.url_.textContent = event.params['url']; - } - - if (event.type == 'BUFFERED_EXTENTS_CHANGED') { - return; - } - this.events_.addEvent(event); - this.metrics_.addEvent(event); - }, - - /** - * Update the summary line and properties table and redraw the canvas. - * @return {HTMLElement} A <li> representing this MediaPlayer. - */ - redraw: function() { - media.appendDictionaryToTable(this.properties, this.propertyTable_); - - this.setAttribute('status', this.properties.state); - - // Don't bother drawing anything if we don't know the total size. - var size = this.properties.total_bytes; - if (!size) { - return; - } - - // Draw the state of BufferedResourceLoader. - this.bufferedEntry_.hidden = false; - var canvas = this.bufferedCanvas_; - var context = canvas.getContext('2d'); - context.fillStyle = '#aaa'; - context.fillRect(0, 0, canvas.width, canvas.height); - - var left = this.properties.buffer_start / size * canvas.width; - var middle = this.properties.buffer_current / size * canvas.width; - var right = this.properties.buffer_end / size * canvas.width; - context.fillStyle = '#a0a'; - context.fillRect(left, 0, middle - left, canvas.height); - context.fillStyle = '#aa0'; - context.fillRect(middle, 0, right - middle, canvas.height); - - // Only show cached file information if we have something. - var cacheEntry = media.cacheEntriesByKey[this.properties.url]; - if (!cacheEntry) { - return; - } - - // Draw cache reads. - this.cacheReadsEntry_.hidden = false; - cacheEntry.drawCacheReadsToCanvas(this.cacheReadsCanvas_); - - // Draw cache writes. - this.cacheWritesEntry_.hidden = false; - cacheEntry.drawCacheWritesToCanvas(this.cacheWritesCanvas_); - }, - }; - - return { - MediaPlayer: MediaPlayer - }; -}); diff --git a/chromium/content/browser/resources/media/metrics.js b/chromium/content/browser/resources/media/metrics.js deleted file mode 100644 index c812d44f56c..00000000000 --- a/chromium/content/browser/resources/media/metrics.js +++ /dev/null @@ -1,116 +0,0 @@ -// 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. - -cr.define('media', function() { - 'use strict'; - - // A set of parameter names. An entry of 'abc' allows metrics to specify - // events with specific values of 'abc'. - var metricProperties = { - 'pipeline_state': true, - }; - - // A set of metrics to measure. The user will see the most recent and average - // measurement of the time between each metric's start and end events. - var metrics = { - 'seek': { - 'start': 'SEEK', - 'end': 'pipeline_state=started' - }, - 'first frame': { - 'start': 'WEBMEDIAPLAYER_CREATED', - 'end': 'pipeline_state=started' - }, - }; - - /** - * This class measures times between the events specified above. It inherits - * <li> and contains a table that displays the measurements. - */ - var Metrics = cr.ui.define('li'); - - Metrics.prototype = { - __proto__: HTMLLIElement.prototype, - - /** - * Decorate this <li> as a Metrics. - */ - decorate: function() { - this.table_ = document.createElement('table'); - var details = document.createElement('details'); - var summary = media.makeElement('summary', 'Metrics:'); - details.appendChild(summary); - details.appendChild(this.table_); - this.appendChild(details); - - var hRow = document.createElement('tr'); - hRow.appendChild(media.makeElement('th', 'Metric:')); - hRow.appendChild(media.makeElement('th', 'Last Measure:')); - hRow.appendChild(media.makeElement('th', 'Average:')); - var header = document.createElement('thead'); - header.appendChild(hRow); - this.table_.appendChild(header); - - for (var metric in metrics) { - var last = document.createElement('td'); - var avg = document.createElement('td'); - this[metric] = { - count: 0, - total: 0, - start: null, - last: last, - avg: avg - }; - var row = document.createElement('tr'); - row.appendChild(media.makeElement('td', metric + ':')); - row.appendChild(last); - row.appendChild(avg); - this.table_.appendChild(row); - } - }, - - /** - * An event has occurred. Update any metrics that refer to this type - * of event. Can be called multiple times by addEvent below if the metrics - * refer to specific parameters. - * @param {Object} event The MediaLogEvent that has occurred. - * @param {string} type The type of event. - */ - addEventInternal: function(event, type) { - for (var metric in metrics) { - var m = this[metric]; - if (type == metrics[metric].start && !m.start) { - m.start = event.ticksMillis; - } else if (type == metrics[metric].end && m.start != null) { - var last = event.ticksMillis - m.start; - m.last.textContent = last.toFixed(1); - m.total += last; - m.count++; - if (m.count > 1) - m.avg.textContent = (m.total / m.count).toFixed(1); - m.start = null; - } - } - }, - - /** - * An event has occurred. Update any metrics that refer to events of this - * type or with this event's parameters. - * @param {Object} event The MediaLogEvent that has occurred. - */ - addEvent: function(event) { - this.addEventInternal(event, event.type); - for (var p in event.params) { - if (p in metricProperties) { - var type = p + '=' + event.params[p]; - this.addEventInternal(event, type); - } - } - }, - }; - - return { - Metrics: Metrics, - }; -}); diff --git a/chromium/content/browser/resources/media/new/integration_test.html b/chromium/content/browser/resources/media/new/integration_test.html deleted file mode 100644 index 3a5225cb647..00000000000 --- a/chromium/content/browser/resources/media/new/integration_test.html +++ /dev/null @@ -1,86 +0,0 @@ -<!-- -Copyright 2013 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. ---> -<!DOCTYPE html> -<html> - <head> - <script src="webui_resource_test.js"></script> - <script src="util.js"></script> - <script src="player_manager.js"></script> - <script src="player_info.js"></script> - <script src="main.js"></script> - </head> - <body> - <script> - window.setUp = function() { - var doNothing = function() {}; - var mockRenderer = { - redrawList: doNothing, - update: doNothing, - select: doNothing - }; - - var manager = new PlayerManager(mockRenderer); - media.initialize(manager); - - window.playerManager = manager; - }; - - // The renderer and player ids are completely arbitrarily. - var TEST_RENDERER = 12; - var TEST_PLAYER = 4; - var TEST_NAME = TEST_RENDERER + ':' + TEST_PLAYER; - - // Correctly use the information from a media event. - window.testOnMediaEvent = function() { - var event = { - ticksMillis: 132, - renderer: TEST_RENDERER, - player: TEST_PLAYER, - params: { - fps: 60, - other: 'hi' - } - }; - - window.media.onMediaEvent(event); - var info = window.playerManager.players_[TEST_NAME]; - - assertEquals(event.ticksMillis, info.firstTimestamp_); - assertEquals(TEST_NAME, info.id); - assertEquals(event.params.fps, info.properties.fps); - }; - - // Remove a player. - window.testOnRenderTerminated = function() { - window.testOnMediaEvent(); - - window.playerManager.shouldRemovePlayer_ = function() { - return true; - }; - - window.media.onRendererTerminated(TEST_RENDERER); - assertEquals(undefined, window.playerManager.players_[TEST_NAME]); - }; - - // Audio Streams are weird, they are handled separately - window.testAddAudioStream = function() { - var event = { - id: 'ID', - status: 'created', - playing: true - }; - - window.media.addAudioStream(event); - - var player = window.playerManager.players_[event.id]; - assertTrue(undefined !== player); - assertEquals(event.playing, player.properties['playing']); - }; - - runTests(); - </script> - </body> -</html> diff --git a/chromium/content/browser/resources/media/new/main.js b/chromium/content/browser/resources/media/new/main.js deleted file mode 100644 index 61f6407bcf5..00000000000 --- a/chromium/content/browser/resources/media/new/main.js +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2013 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. - -/** - * A global object that gets used by the C++ interface. - */ -var media = (function() { - 'use strict'; - - var manager = null; - - /** - * Users of |media| must call initialize prior to calling other methods. - */ - function initialize(playerManager) { - manager = playerManager; - } - - /** - * Call to modify or add a system property. - */ - function onSystemProperty(timestamp, key, value) { - console.log('System properties not yet implemented'); - } - - /** - * Call to modify or add a property on a player. - */ - function onPlayerProperty(id, timestamp, key, value) { - manager.updatePlayerInfo(id, timestamp, key, value); - } - - function onPlayerPropertyNoRecord(id, timestamp, key, value) { - manager.updatePlayerInfoNoRecord(id, timestamp, key, value); - } - - /** - * Call to add a player. - */ - function onPlayerOpen(id, timestamp) { - manager.addPlayer(id, timestamp); - } - - /** - * Call to remove a player. - */ - function onPlayerClose(id) { - manager.removePlayer(id); - } - - var media = { - onSystemProperty: onSystemProperty, - onPlayerProperty: onPlayerProperty, - onPlayerPropertyNoRecord: onPlayerPropertyNoRecord, - onPlayerOpen: onPlayerOpen, - onPlayerClose: onPlayerClose, - - initialize: initialize - }; - - // Everything beyond this point is for backwards compatibility reasons. - // It will go away when the backend is updated. - - media.onNetUpdate = function(update) { - // TODO(tyoverby): Implement - }; - - media.onRendererTerminated = function(renderId) { - util.object.forEach(manager.players_, function(playerInfo, id) { - if (playerInfo.properties['render_id'] == renderId) { - media.onPlayerClose(id); - } - }); - }; - - // For whatever reason, addAudioStream is also called on - // the removal of audio streams. - media.addAudioStream = function(event) { - switch (event.status) { - case 'created': - media.onPlayerOpen(event.id); - // We have to simulate the timestamp since it isn't provided to us. - media.onPlayerProperty( - event.id, (new Date()).getTime(), 'playing', event.playing); - break; - case 'closed': - media.onPlayerClose(event.id); - break; - } - }; - media.onItemDeleted = function() { - // This only gets called when an audio stream is removed, which - // for whatever reason is also handled by addAudioStream... - // Because it is already handled, we can safely ignore it. - }; - - media.onMediaEvent = function(event) { - var source = event.renderer + ':' + event.player; - - // Although this gets called on every event, there is nothing we can do - // about this because there is no onOpen event. - media.onPlayerOpen(source); - media.onPlayerPropertyNoRecord( - source, event.ticksMillis, 'render_id', event.renderer); - media.onPlayerPropertyNoRecord( - source, event.ticksMillis, 'player_id', event.player); - - var propertyCount = 0; - util.object.forEach(event.params, function(value, key) { - key = key.trim(); - - // These keys get spammed *a lot*, so put them on the display - // but don't log list. - if (key === 'buffer_start' || - key === 'buffer_end' || - key === 'buffer_current' || - key === 'is_downloading_data') { - media.onPlayerPropertyNoRecord( - source, event.ticksMillis, key, value); - } else { - media.onPlayerProperty(source, event.ticksMillis, key, value); - } - propertyCount += 1; - }); - - if (propertyCount === 0) { - media.onPlayerProperty( - source, event.ticksMillis, 'EVENT', event.type); - } - }; - - return media; -}()); diff --git a/chromium/content/browser/resources/media/new/media_internals.html b/chromium/content/browser/resources/media/new/media_internals.html deleted file mode 100644 index 0e95353aeef..00000000000 --- a/chromium/content/browser/resources/media/new/media_internals.html +++ /dev/null @@ -1,18 +0,0 @@ -<!-- -Copyright 2013 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. ---> -<!DOCTYPE html> -<html i18n-values="dir:textdirection"> -<head> - <meta charset="utf-8"> - <title i18n-content="Media Internals"></title> - - <script src="chrome://media-internals/media_internals.js"></script> -</head> - -<body> - Hello World -</body> -</html> diff --git a/chromium/content/browser/resources/media/new/media_internals.js b/chromium/content/browser/resources/media/new/media_internals.js deleted file mode 100644 index 103ef74fbc0..00000000000 --- a/chromium/content/browser/resources/media/new/media_internals.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 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. - -var media = {}; - -var doNothing = function() {}; - -// Silence the backend calls. -media.initialize = doNothing; -media.addAudioStream = doNothing; -media.cacheEntriesByKey = doNothing; -media.onReceiveEverything = doNothing; -media.onItemDeleted = doNothing; -media.onRendererTerminated = doNothing; -media.onNetUpdate = doNothing; -media.onReceiveConstants = doNothing; -media.onMediaEvent = doNothing; diff --git a/chromium/content/browser/resources/media/new/player_info_test.html b/chromium/content/browser/resources/media/new/player_info_test.html deleted file mode 100644 index 46cc05ee3d9..00000000000 --- a/chromium/content/browser/resources/media/new/player_info_test.html +++ /dev/null @@ -1,146 +0,0 @@ -<!-- -Copyright 2013 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. ---> -<!DOCTYPE html> -<html> - <head> - <script src="webui_resource_test.js"></script> - <script src="player_manager.js"></script> - <script src="player_info.js"></script> - </head> - <body> - <script> - window.setUp = function() { - window.pi = new PlayerInfo('example_id'); - }; - - window.tearDown = function() { - window.pi = null; - }; - - // Test that an ID is set correctly. - window.testConstructorStringID = function() { - assertEquals('example_id', window.pi.id); - }; - - // Test that numerical IDs are valid. - window.testConstructorNumberId = function() { - var pi = new PlayerInfo(5); - assertEquals(5, pi.id); - }; - - // Make sure that a new PlayerInfo has no events. - window.testEmptyEvents = function() { - assertEquals(0, window.pi.allEvents.length); - }; - - // Check that the most recent property gets updated. - window.testAddProperty = function() { - var key = 'key', - value = 'value', - value2 = 'value2'; - - window.pi.addProperty(0, key, value); - assertEquals(value, window.pi.properties[key]); - - window.pi.addProperty(0, key, value2); - assertEquals(value2, window.pi.properties[key]); - - }; - - // Make sure that the first timestamp that gets sent - // is recorded as the base timestamp. - window.testFirstTimestamp = function() { - var pi = new PlayerInfo('example_ID'); - var timestamp = 5000; - pi.addProperty(timestamp, 'key', 'value'); - - assertEquals(timestamp, pi.firstTimestamp_); - }; - - // Adding a property with a non-string key should - // throw an exception. - window.testWrongKeyType = function() { - var pi = new PlayerInfo('example_ID'); - assertThrows(function() { - pi.addProperty(0, 5, 'some value'); - }); - }; - - // Subsequent events should have their log offset based - // on the first timestamp added. - window.testAddPropertyTimestampOffset = function() { - var firstTimestamp = 500, - secondTimestamp = 550, - deltaT = secondTimestamp - firstTimestamp, - key = 'key', - value = 'value'; - - var pi = new PlayerInfo('example_ID'); - pi.addProperty(firstTimestamp, key, value); - pi.addProperty(secondTimestamp, key, value); - - assertEquals(firstTimestamp, pi.firstTimestamp_); - assertEquals(0, pi.allEvents[0].time); - assertEquals(deltaT, pi.allEvents[1].time); - - assertTrue(undefined !== pi.pastValues[key]); - - console.log(pi.pastValues); - - assertEquals(0, pi.pastValues[key][0].time); - assertEquals(deltaT, pi.pastValues[key][1].time); - }; - - // Check to make sure that properties are correctly - // added to the relevant pastValues array. - window.testAddPropertyPastValues = function() { - var pi = new PlayerInfo('example_ID'), - timestamp = 50, - key = 'key', - value = 'value'; - - pi.addProperty(timestamp, key, value); - - assertEquals(value, pi.pastValues[key][0].value); - assertEquals(key, pi.pastValues[key][0].key); - assertEquals(0, pi.pastValues[key][0].time); - }; - - // The list of all events should be recorded in correctly. - window.testAllEvents = function() { - var pi = new PlayerInfo('example_ID'), - timestamp = 50, - key = 'key', - value = 'value', - key2 = 'key2', - value2 = 'value2'; - - pi.addProperty(timestamp, key, value); - assertEquals(value, pi.allEvents[0].value); - assertEquals(key, pi.allEvents[0].key); - - pi.addProperty(timestamp, key2, value2); - assertEquals(value2, pi.allEvents[1].value); - assertEquals(key2, pi.allEvents[1].key); - }; - - // Using noRecord should make it not show up in allEvents, - // but it should still show up in pastValues[key]. - window.testNoRecord = function() { - var pi = new PlayerInfo('example_ID'), - timestamp = 50, - key = 'key', - value = 'value'; - pi.addPropertyNoRecord(timestamp, key, value); - - assertEquals(value, pi.properties[key]); - assertEquals(0, pi.allEvents.length); - assertEquals(1, pi.pastValues[key].length); - }; - runTests(); - </script> - </body> -</html> diff --git a/chromium/content/browser/resources/media/new/player_manager.js b/chromium/content/browser/resources/media/new/player_manager.js deleted file mode 100644 index 3de93357f98..00000000000 --- a/chromium/content/browser/resources/media/new/player_manager.js +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2013 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. - -/** - * @fileoverview Keeps track of all the existing - * PlayerProperty objects and is the entry-point for messages from the backend. - */ -var PlayerManager = (function() { - 'use strict'; - - function PlayerManager(renderManager) { - this.players_ = {}; - this.renderman_ = renderManager; - renderManager.playerManager = this; - - this.shouldRemovePlayer_ = function() { - // This is only temporary until we get the UI hooked up. - return true; - }; - } - - PlayerManager.prototype = { - - /** - * Adds a player to the list of players to manage. - */ - addPlayer: function(id) { - if (this.players_[id]) { - return; - } - // Make the PlayerProperty and add it to the mapping - this.players_[id] = new PlayerInfo(id); - - this.renderman_.redrawList(); - }, - - /** - * Attempts to remove a player from the UI. - * @param id The ID of the player to remove. - */ - removePlayer: function(id) { - // Look at the check box to see if we should actually - // remove it from the UI - if (this.shouldRemovePlayer_()) { - delete this.players_[id]; - this.renderman_.redrawList(); - } else if (this.players_[id]) { - // Set a property on it to be removed at a later time - this.players_[id].toRemove = true; - } - }, - - /** - * Selects a player and displays it on the UI. - * This method is called from the UI. - * @param id The ID of the player to display. - */ - selectPlayer: function(id) { - if (!this.players_[id]) { - throw new Error('[selectPlayer] Id ' + id + ' does not exist.'); - } - - this.renderman_.select(id); - }, - - updatePlayerInfoNoRecord: function(id, timestamp, key, value) { - if (!this.players_[id]) { - console.error('[updatePlayerInfo] Id ' + id + - ' does not exist'); - return; - } - - this.players_[id].addPropertyNoRecord(timestamp, key, value); - - // If we can potentially rename the player, do so. - if (key === 'name' || key === 'url') { - this.renderman_.redrawList(); - } - - this.renderman_.update(); - }, - - /** - * - * @param id The unique ID that identifies the player to be updated. - * @param timestamp The timestamp of when the change occured. This - * timestamp is *not* normalized. - * @param key The name of the property to be added/changed. - * @param value The value of the property. - */ - updatePlayerInfo: function(id, timestamp, key, value) { - if (!this.players_[id]) { - console.error('[updatePlayerInfo] Id ' + id + - ' does not exist'); - return; - } - - this.players_[id].addProperty(timestamp, key, value); - - // If we can potentially rename the player, do so. - if (key === 'name' || key === 'url') { - this.renderman_.redrawList(); - } - - this.renderman_.update(); - } - }; - - return PlayerManager; -}()); diff --git a/chromium/content/browser/resources/media/new/player_manager_test.html b/chromium/content/browser/resources/media/new/player_manager_test.html deleted file mode 100644 index eff78b53c59..00000000000 --- a/chromium/content/browser/resources/media/new/player_manager_test.html +++ /dev/null @@ -1,155 +0,0 @@ -<!-- -Copyright 2013 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. ---> -<!DOCTYPE html> -<html> - <head> - <script src="webui_resource_test.js"></script> - <script src="player_manager.js"></script> - <script src="player_info.js"></script> - </head> - <body> - <script> - var doNothing = function() { - }; - - var emptyRenderMan = { - redrawList: doNothing, - update: doNothing, - select: doNothing - }; - - window.setUp = function() { - window.pm = new PlayerManager(emptyRenderMan); - }; - - window.tearDown = function() { - window.pm = null; - }; - - // Test a normal case of .addPlayer - window.testAddPlayer = function() { - window.pm.addPlayer('someid'); - assertTrue(undefined !== window.pm.players_['someid']); - }; - - // Make sure that adding a player forces a redraw - // on the renderManager. - window.testAddPlayerForceRedraw = function() { - var redrew = false; - var mockRenderManager = { - redrawList: function() { - redrew = true; - } - }; - var pm = new PlayerManager(mockRenderManager); - - pm.addPlayer('someid'); - assertTrue(redrew); - }; - - // On occasion, the backend will add an existing ID multiple times. - // make sure this doesn't break anything. - window.testAddPlayerAlreadyExisting = function() { - window.pm.addPlayer('someid'); - window.pm.addPlayer('someid'); - assertTrue(undefined !== window.pm.players_['someid']); - }; - - // If the removal is set, make sure that a player - // gets removed from the PlayerManager. - window.testRemovePlayerShouldRemove = function() { - // Because we don't have the checkbox. - window.pm.shouldRemovePlayer_ = function() { - return true; - }; - window.pm.addPlayer('someid'); - assertTrue(undefined !== window.pm.players_['someid']); - window.pm.removePlayer('someid'); - assertTrue(undefined === window.pm.players_['someid']); - }; - - // On the removal of a player, the renderer should be forced - // to redraw the list. - window.testRemovePlayerRedraw = function() { - var redrew = false; - - var fakeObj = { - redrawList: function() { - redrew = true; - } - }; - - var pm = new PlayerManager(fakeObj); - // Because we don't have the checkbox; - pm.shouldRemovePlayer_ = function() { - return true; - }; - - - pm.addPlayer('someid'); - assertTrue(undefined !== pm.players_['someid']); - pm.removePlayer('someid'); - assertTrue(undefined === pm.players_['someid']); - - assertTrue(redrew); - }; - - // If you shouldn't remove the player, the player shouldn't be - // removed. - window.testRemovePlayerNoRemove = function() { - window.pm = new PlayerManager(emptyRenderMan); - // Because we don't have the checkbox; - window.pm.shouldRemovePlayer_ = function() { - return false; - }; - window.pm.addPlayer('someid'); - assertTrue(undefined !== window.pm.players_['someid']); - window.pm.removePlayer('someid'); - assertTrue(undefined !== window.pm.players_['someid']); - }; - - - // Removing a nonexistant player shouldn't break anything - // The backend also occasionally does this. - window.testRemovePlayerNonExistant = function() { - // Because we don't have the checkbox; - window.pm.shouldRemovePlayer_ = function() { - return false; - }; - window.pm.removePlayer('someid'); - assertTrue(undefined === window.pm.players_['someid']); - }; - - // Trying to select a non-existant player should throw - // an exception - window.testSelectNonExistant = function() { - assertThrows(function() { - window.pm.selectPlayer('someId'); - }); - }; - - // Selecting an existing player should trigger a redraw - window.testSelectExistingPlayer = function() { - var selected = false; - var redrew = false; - var pm = new PlayerManager({ - select: function() { - selected = true; - }, - redrawList: function() { - redrew = true; - } - }); - pm.addPlayer('someId'); - pm.selectPlayer('someId'); - - assertTrue(selected); - assertTrue(redrew); - }; - runTests(); - </script> - </body> -</html> diff --git a/chromium/content/browser/resources/media/new/util.js b/chromium/content/browser/resources/media/new/util.js deleted file mode 100644 index 5909e9ee1eb..00000000000 --- a/chromium/content/browser/resources/media/new/util.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 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. - -/** - * @fileoverview Some utility functions that don't belong anywhere else in the - * code. - */ - -var util = (function() { - var util = {}; - util.object = {}; - /** - * Calls a function for each element in an object/map/hash. - * - * @param obj The object to iterate over. - * @param f The function to call on every value in the object. F should have - * the following arguments: f(value, key, object) where value is the value - * of the property, key is the corresponding key, and obj is the object that - * was passed in originally. - * @param optObj The object use as 'this' within f. - */ - util.object.forEach = function(obj, f, optObj) { - 'use strict'; - var key; - for (key in obj) { - if (obj.hasOwnProperty(key)) { - f.call(optObj, obj[key], key, obj); - } - } - }; - - return util; -}()); diff --git a/chromium/content/browser/resources/media/new/webui_resource_test.js b/chromium/content/browser/resources/media/new/webui_resource_test.js deleted file mode 100644 index 6b05a305a70..00000000000 --- a/chromium/content/browser/resources/media/new/webui_resource_test.js +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2013 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. - -/** - * Tests that an observation matches the expected value. - * @param {Object} expected The expected value. - * @param {Object} observed The actual value. - * @param {string=} opt_message Optional message to include with a test - * failure. - */ -function assertEquals(expected, observed, opt_message) { - if (observed !== expected) { - var message = 'Assertion Failed\n Observed: ' + observed + - '\n Expected: ' + expected; - if (opt_message) - message = message + '\n ' + opt_message; - throw new Error(message); - } -} - -/** - * Verifies that a test result is true. - * @param {boolean} observed The observed value. - * @param {string=} opt_message Optional message to include with a test - * failure. - */ -function assertTrue(observed, opt_message) { - assertEquals(true, observed, opt_message); -} - -/** - * Verifies that a test result is false. - * @param {boolean} observed The observed value. - * @param {string=} opt_message Optional message to include with a test - * failure. - */ -function assertFalse(observed, opt_message) { - assertEquals(false, observed, opt_message); -} - -/** - * Verifies that the observed and reference values differ. - * @param {Object} reference The target value for comparison. - * @param {Object} observed The test result. - * @param {string=} opt_message Optional message to include with a test - * failure. - */ -function assertNotEqual(reference, observed, opt_message) { - if (observed === reference) { - var message = 'Assertion Failed\n Observed: ' + observed + - '\n Reference: ' + reference; - if (opt_message) - message = message + '\n ' + opt_message; - throw new Error(message); - } -} - -/** - * Verifies that a test evaluation results in an exception. - * @param {!Function} f The test function. - */ -function assertThrows(f) { - var triggeredError = false; - try { - f(); - } catch (err) { - triggeredError = true; - } - if (!triggeredError) - throw new Error('Assertion Failed: throw expected.'); -} - -/** - * Verifies that the contents of the expected and observed arrays match. - * @param {!Array} expected The expected result. - * @param {!Array} observed The actual result. - */ -function assertArrayEquals(expected, observed) { - var v1 = Array.prototype.slice.call(expected); - var v2 = Array.prototype.slice.call(observed); - var equal = v1.length == v2.length; - if (equal) { - for (var i = 0; i < v1.length; i++) { - if (v1[i] !== v2[i]) { - equal = false; - break; - } - } - } - if (!equal) { - var message = - ['Assertion Failed', 'Observed: ' + v2, 'Expected: ' + v1].join('\n '); - throw new Error(message); - } -} - -/** - * Verifies that the expected and observed result have the same content. - * @param {*} expected The expected result. - * @param {*} observed The actual result. - */ -function assertDeepEquals(expected, observed, opt_message) { - if (typeof expected == 'object' && expected != null) { - assertNotEqual(null, observed); - for (var key in expected) { - assertTrue(key in observed, opt_message); - assertDeepEquals(expected[key], observed[key], opt_message); - } - for (var key in observed) { - assertTrue(key in expected, opt_message); - } - } else { - assertEquals(expected, observed, opt_message); - } -} - -/** - * Defines runTests. - */ -(function(exports) { - /** - * List of test cases. - * @type {Array.<string>} List of function names for tests to run. - */ - var testCases = []; - - /** - * Indicates if all tests have run successfully. - * @type {boolean} - */ - var cleanTestRun = true; - - /** - * Armed during setup of a test to call the matching tear down code. - * @type {Function} - */ - var pendingTearDown = null; - - /** - * Runs all functions starting with test and reports success or - * failure of the test suite. - */ - function runTests() { - for (var name in window) { - if (typeof window[name] == 'function' && /^test/.test(name)) - testCases.push(name); - } - if (!testCases.length) { - console.error('Failed to find test cases.'); - cleanTestRun = false; - } - continueTesting(); - } - - function reportPass(name) { - 'use strict'; - var text = document.createTextNode(name + ': PASSED'); - var span = document.createElement('span'); - span.appendChild(text); - document.body.appendChild(span); - document.body.appendChild(document.createElement('br')); - } - - function reportFail(name) { - 'use strict'; - var text = document.createTextNode(name + ': =========FAILED======='); - var span = document.createElement('span'); - span.appendChild(text); - document.body.appendChild(span); - document.body.appendChild(document.createElement('br')); - } - - /** - * Runs the next test in the queue. Reports the test results if the queue is - * empty. - */ - function continueTesting() { - if (pendingTearDown) { - pendingTearDown(); - pendingTearDown = null; - } - if (testCases.length > 0) { - var fn = testCases.pop(); - var isAsyncTest = window[fn].length; - try { - if (window.setUp) - window.setUp(); - pendingTearDown = window.tearDown; - window[fn](continueTesting); - reportPass(fn); - } catch (err) { - reportFail(fn); - console.error('Failure in test ' + fn + '\n' + err); - console.log(err.stack); - cleanTestRun = false; - } - // Asynchronous tests must manually call continueTesting when complete. - if (!isAsyncTest) - continueTesting(); - } - if (testCases.length) { - domAutomationController.setAutomationId(1); - domAutomationController.send('PENDING'); - } - }; - - exports.runTests = runTests; -})(this); - diff --git a/chromium/content/browser/resources/media/new/player_info.js b/chromium/content/browser/resources/media/player_info.js index af1f1944518..af1f1944518 100644 --- a/chromium/content/browser/resources/media/new/player_info.js +++ b/chromium/content/browser/resources/media/player_info.js diff --git a/chromium/content/browser/resources/media/util.js b/chromium/content/browser/resources/media/util.js index c61ae0e9793..29cc1b152e3 100644 --- a/chromium/content/browser/resources/media/util.js +++ b/chromium/content/browser/resources/media/util.js @@ -1,74 +1,47 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2013 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. -cr.define('media', function() { - 'use strict'; +/** + * @fileoverview Some utility functions that don't belong anywhere else in the + * code. + */ +var util = (function() { + var util = {}; + util.object = {}; /** - * The width and height of a bar drawn on a file canvas in pixels. + * Calls a function for each element in an object/map/hash. + * + * @param obj The object to iterate over. + * @param f The function to call on every value in the object. F should have + * the following arguments: f(value, key, object) where value is the value + * of the property, key is the corresponding key, and obj is the object that + * was passed in originally. + * @param optObj The object use as 'this' within f. */ - var BAR_WIDTH = 500; - var BAR_HEIGHT = 16; - - /** - * Draws a 1px white horizontal line across |context|. - */ - function drawLine(context, top) { - context.moveTo(0, top); - context.lineTo(BAR_WIDTH, top); - context.strokeStyle = '#fff'; - context.stroke(); - } - - /** - * Creates an HTMLElement of type |type| with textContent |content|. - * @param {string} type The type of element to create. - * @param {string} content The content to place in the element. - * @return {HTMLElement} A newly initialized element. - */ - function makeElement(type, content) { - var element = document.createElement(type); - element.textContent = content; - return element; - } - - /** - * Creates a new <li> containing a <details> with a <summary> and sets - * properties to reference them. - * @return {Object} The new <li>. - */ - function createDetailsLi() { - var li = document.createElement('li'); - li.details = document.createElement('details'); - li.summary = document.createElement('summary'); - li.appendChild(li.details); - li.details.appendChild(li.summary); - return li - } - - /** - * Appends each key-value pair in a dictionary to a row in a table. - * @param {Object} dict The dictionary to append. - * @param {HTMLElement} table The <table> element to append to. - */ - function appendDictionaryToTable(dict, table) { - table.textContent = ''; - for (var key in dict) { - var tr = document.createElement('tr'); - tr.appendChild(makeElement('td', key + ':')); - tr.appendChild(makeElement('td', dict[key])); - table.appendChild(tr); + util.object.forEach = function(obj, f, optObj) { + 'use strict'; + var key; + for (key in obj) { + if (obj.hasOwnProperty(key)) { + f.call(optObj, obj[key], key, obj); + } + } + }; + util.millisecondsToString = function(timeMillis) { + function pad(num) { + num = num.toString(); + if (num.length < 2) { + return '0' + num; + } + return num; } - return table; - } - return { - BAR_WIDTH: BAR_WIDTH, - BAR_HEIGHT: BAR_HEIGHT, - drawLine: drawLine, - makeElement: makeElement, - createDetailsLi: createDetailsLi, - appendDictionaryToTable: appendDictionaryToTable + var date = new Date(timeMillis); + return pad(date.getUTCHours()) + ':' + pad(date.getUTCMinutes()) + ':' + + pad(date.getUTCSeconds()) + ' ' + pad((date.getMilliseconds()) % 1000); }; -}); + + return util; +}()); diff --git a/chromium/content/browser/resources/media/webrtc_internals.js b/chromium/content/browser/resources/media/webrtc_internals.js index fd2d34a3faf..5bc56da3ebe 100644 --- a/chromium/content/browser/resources/media/webrtc_internals.js +++ b/chromium/content/browser/resources/media/webrtc_internals.js @@ -225,6 +225,8 @@ function updateAllPeerConnections(data) { var peerConnection = addPeerConnection(data[i]); var log = data[i].log; + if (!log) + continue; for (var j = 0; j < log.length; ++j) { addPeerConnectionUpdate(peerConnection, log[j]); } diff --git a/chromium/content/browser/security_exploit_browsertest.cc b/chromium/content/browser/security_exploit_browsertest.cc index bc554c2bb82..2eb7b51885b 100644 --- a/chromium/content/browser/security_exploit_browsertest.cc +++ b/chromium/content/browser/security_exploit_browsertest.cc @@ -9,7 +9,7 @@ #include "content/public/browser/notification_types.h" #include "content/public/common/content_switches.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" diff --git a/chromium/content/browser/service_worker/OWNERS b/chromium/content/browser/service_worker/OWNERS new file mode 100644 index 00000000000..633b8a4fd96 --- /dev/null +++ b/chromium/content/browser/service_worker/OWNERS @@ -0,0 +1,3 @@ +alecflett@chromium.org +kinuko@chromium.org +michaeln@chromium.org diff --git a/chromium/content/browser/service_worker/service_worker_context.cc b/chromium/content/browser/service_worker/service_worker_context.cc new file mode 100644 index 00000000000..ed9f669d7f4 --- /dev/null +++ b/chromium/content/browser/service_worker/service_worker_context.cc @@ -0,0 +1,25 @@ +// Copyright 2013 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/browser/service_worker/service_worker_context.h" + +#include "base/files/file_path.h" +#include "webkit/browser/quota/quota_manager.h" + +namespace content { + +const base::FilePath::CharType kServiceWorkerDirectory[] = + FILE_PATH_LITERAL("ServiceWorker"); + +ServiceWorkerContext::ServiceWorkerContext( + const base::FilePath& path, + quota::QuotaManagerProxy* quota_manager_proxy) + : quota_manager_proxy_(quota_manager_proxy) { + if (!path.empty()) + path_ = path.Append(kServiceWorkerDirectory); +} + +ServiceWorkerContext::~ServiceWorkerContext() {} + +} // namespace content diff --git a/chromium/content/browser/service_worker/service_worker_context.h b/chromium/content/browser/service_worker/service_worker_context.h new file mode 100644 index 00000000000..5b290b7a317 --- /dev/null +++ b/chromium/content/browser/service_worker/service_worker_context.h @@ -0,0 +1,44 @@ +// Copyright 2013 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_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_ + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" + +namespace base { +class FilePath; +} + +namespace quota { +class QuotaManagerProxy; +} + +namespace content { + +// This class manages metadata associated with all service workers, +// including: +// - persistent storage of pattern -> service worker scripts +// - initialization and initial installation of service workers +// - dispatching of non-fetch events to service workers +class ServiceWorkerContext + : public base::RefCountedThreadSafe<ServiceWorkerContext> { + public: + // This is owned by the StoragePartition, which will supply it with + // the local path on disk. + ServiceWorkerContext(const base::FilePath& path, + quota::QuotaManagerProxy* quota_manager_proxy); + + private: + friend class base::RefCountedThreadSafe<ServiceWorkerContext>; + ~ServiceWorkerContext(); + + scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_; + base::FilePath path_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_ diff --git a/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc b/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc new file mode 100644 index 00000000000..c531e915e88 --- /dev/null +++ b/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc @@ -0,0 +1,50 @@ +// Copyright 2013 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/browser/service_worker/service_worker_dispatcher_host.h" + +#include "content/browser/service_worker/service_worker_context.h" +#include "content/common/service_worker_messages.h" +#include "ipc/ipc_message_macros.h" +#include "url/gurl.h" + +namespace content { + +ServiceWorkerDispatcherHost::ServiceWorkerDispatcherHost( + ServiceWorkerContext* context) + : context_(context) {} + +ServiceWorkerDispatcherHost::~ServiceWorkerDispatcherHost() {} + +bool ServiceWorkerDispatcherHost::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + + if (IPC_MESSAGE_CLASS(message) != ServiceWorkerMsgStart) + return false; + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX( + ServiceWorkerDispatcherHost, message, *message_was_ok) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_RegisterServiceWorker, + OnRegisterServiceWorker) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_UnregisterServiceWorker, + OnUnregisterServiceWorker) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void ServiceWorkerDispatcherHost::OnRegisterServiceWorker(int32 registry_id, + const string16& scope, + const GURL& script_url) { + // TODO(alecflett): Enforce that script_url must have the same + // origin as the registering document. +} + +void ServiceWorkerDispatcherHost::OnUnregisterServiceWorker( + int32 registry_id, + const string16& scope) {} + +} // namespace content diff --git a/chromium/content/browser/service_worker/service_worker_dispatcher_host.h b/chromium/content/browser/service_worker/service_worker_dispatcher_host.h new file mode 100644 index 00000000000..36927819f69 --- /dev/null +++ b/chromium/content/browser/service_worker/service_worker_dispatcher_host.h @@ -0,0 +1,40 @@ +// Copyright 2013 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_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISPATCHER_HOST_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISPATCHER_HOST_H_ + +#include "content/public/browser/browser_message_filter.h" + +class GURL; + +namespace content { + +class ServiceWorkerContext; + +class ServiceWorkerDispatcherHost : public BrowserMessageFilter { + public: + explicit ServiceWorkerDispatcherHost(ServiceWorkerContext* context); + + // BrowserIOMessageFilter implementation + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) OVERRIDE; + + protected: + virtual ~ServiceWorkerDispatcherHost(); + + private: + // IPC Message handlers + + void OnRegisterServiceWorker(int32 registry_id, + const string16& scope, + const GURL& script_url); + void OnUnregisterServiceWorker(int32 registry_id, const string16& scope); + + scoped_refptr<ServiceWorkerContext> context_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISPATCHER_HOST_H_ diff --git a/chromium/content/browser/session_history_browsertest.cc b/chromium/content/browser/session_history_browsertest.cc index d0bd95001a8..7d006b606f6 100644 --- a/chromium/content/browser/session_history_browsertest.cc +++ b/chromium/content/browser/session_history_browsertest.cc @@ -12,7 +12,7 @@ #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -89,7 +89,7 @@ class SessionHistoryTest : public ContentBrowserTest { } GURL GetTabURL() { - return shell()->web_contents()->GetURL(); + return shell()->web_contents()->GetLastCommittedURL(); } GURL GetURL(const std::string file) { diff --git a/chromium/content/browser/site_instance_impl.cc b/chromium/content/browser/site_instance_impl.cc index 42680674d76..ade96cba840 100644 --- a/chromium/content/browser/site_instance_impl.cc +++ b/chromium/content/browser/site_instance_impl.cc @@ -26,7 +26,7 @@ static bool IsURLSameAsAnySiteInstance(const GURL& url) { // We treat javascript: as the same site as any URL since it is actually // a modifier on existing pages. - if (url.SchemeIs(chrome::kJavaScriptScheme)) + if (url.SchemeIs(kJavaScriptScheme)) return true; return url == GURL(kChromeUICrashURL) || diff --git a/chromium/content/browser/site_per_process_browsertest.cc b/chromium/content/browser/site_per_process_browsertest.cc index 3485fc51fed..e37ac97cce8 100644 --- a/chromium/content/browser/site_per_process_browsertest.cc +++ b/chromium/content/browser/site_per_process_browsertest.cc @@ -13,7 +13,7 @@ #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" diff --git a/chromium/content/browser/speech/OWNERS b/chromium/content/browser/speech/OWNERS index b38caa6f1ff..2c6195ec94b 100644 --- a/chromium/content/browser/speech/OWNERS +++ b/chromium/content/browser/speech/OWNERS @@ -1,3 +1,4 @@ hans@chromium.org +primiano@chromium.org tommi@chromium.org xians@chromium.org diff --git a/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc b/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc index 08a3df7cdb7..397250631fb 100644 --- a/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc +++ b/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc @@ -6,7 +6,9 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/safe_numerics.h" #include "base/strings/utf_string_conversions.h" +#include "base/sys_byteorder.h" #include "content/browser/speech/audio_buffer.h" #include "content/browser/speech/google_streaming_remote_engine.h" #include "content/browser/speech/proto/google_streaming_api.pb.h" @@ -17,6 +19,8 @@ #include "net/url_request/url_request_status.h" #include "testing/gtest/include/gtest/gtest.h" +using base::HostToNet32; +using base::checked_numeric_cast; using net::URLRequestStatus; using net::TestURLFetcher; using net::TestURLFetcherFactory; @@ -62,7 +66,6 @@ class GoogleStreamingRemoteEngineTest : public SpeechRecognitionEngineDelegate, const SpeechRecognitionResults& b); static std::string SerializeProtobufResponse( const proto::SpeechRecognitionEvent& msg); - static std::string ToBigEndian32(uint32 value); TestURLFetcher* GetUpstreamFetcher(); TestURLFetcher* GetDownstreamFetcher(); @@ -487,17 +490,10 @@ std::string GoogleStreamingRemoteEngineTest::SerializeProtobufResponse( // Prepend 4 byte prefix length indication to the protobuf message as // envisaged by the google streaming recognition webservice protocol. - msg_string.insert(0, ToBigEndian32(msg_string.size())); - return msg_string; -} + uint32 prefix = HostToNet32(checked_numeric_cast<uint32>(msg_string.size())); + msg_string.insert(0, reinterpret_cast<char*>(&prefix), sizeof(prefix)); -std::string GoogleStreamingRemoteEngineTest::ToBigEndian32(uint32 value) { - char raw_data[4]; - raw_data[0] = static_cast<uint8>((value >> 24) & 0xFF); - raw_data[1] = static_cast<uint8>((value >> 16) & 0xFF); - raw_data[2] = static_cast<uint8>((value >> 8) & 0xFF); - raw_data[3] = static_cast<uint8>(value & 0xFF); - return std::string(raw_data, sizeof(raw_data)); + return msg_string; } } // namespace content diff --git a/chromium/content/browser/speech/speech_recognition_browsertest.cc b/chromium/content/browser/speech/input_tag_speech_browsertest.cc index 8e4c7a84ed7..0a1b2a9ca86 100644 --- a/chromium/content/browser/speech/speech_recognition_browsertest.cc +++ b/chromium/content/browser/speech/input_tag_speech_browsertest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 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. @@ -18,14 +18,14 @@ #include "content/public/common/url_constants.h" #include "content/public/test/fake_speech_recognition_manager.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "third_party/WebKit/public/web/WebInputEvent.h" namespace content { -class SpeechRecognitionBrowserTest : public ContentBrowserTest { +class InputTagSpeechBrowserTest : public ContentBrowserTest { public: // ContentBrowserTest methods virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { @@ -69,7 +69,7 @@ class SpeechRecognitionBrowserTest : public ContentBrowserTest { // then sets the URL fragment as 'pass' if it received the expected string. LoadAndStartSpeechRecognitionTest(filename); - EXPECT_EQ("pass", shell()->web_contents()->GetURL().ref()); + EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref()); } // ContentBrowserTest methods. @@ -94,7 +94,7 @@ class SpeechRecognitionBrowserTest : public ContentBrowserTest { }; SpeechRecognitionManager* - SpeechRecognitionBrowserTest::speech_recognition_manager_ = NULL; + InputTagSpeechBrowserTest::speech_recognition_manager_ = NULL; // TODO(satish): Once this flakiness has been fixed, add a second test here to // check for sending many clicks in succession to the speech button and verify @@ -105,19 +105,19 @@ SpeechRecognitionManager* // another test here to check that when speech recognition is in progress and // a renderer crashes, we get a call to // SpeechRecognitionManager::CancelAllRequestsWithDelegate. -IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, TestBasicRecognition) { +IN_PROC_BROWSER_TEST_F(InputTagSpeechBrowserTest, TestBasicRecognition) { RunSpeechRecognitionTest("basic_recognition.html"); EXPECT_TRUE(fake_speech_recognition_manager_.grammar().empty()); } -IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, GrammarAttribute) { +IN_PROC_BROWSER_TEST_F(InputTagSpeechBrowserTest, GrammarAttribute) { RunSpeechRecognitionTest("grammar_attribute.html"); EXPECT_EQ("http://example.com/grammar.xml", fake_speech_recognition_manager_.grammar()); } // Flaky on Linux, Windows and Mac http://crbug.com/140765. -IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, DISABLED_TestCancelAll) { +IN_PROC_BROWSER_TEST_F(InputTagSpeechBrowserTest, DISABLED_TestCancelAll) { // The test checks that the cancel-all callback gets issued when a session // is pending, so don't send a fake response. // We are not expecting a navigation event being raised from the JS of the diff --git a/chromium/content/browser/speech/speech_recognizer_impl.cc b/chromium/content/browser/speech/speech_recognizer_impl.cc index 2081b2f982d..2dae43087eb 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl.cc +++ b/chromium/content/browser/speech/speech_recognizer_impl.cc @@ -564,7 +564,7 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) { new OnDataConverter(input_parameters, output_parameters)); audio_controller_ = AudioInputController::Create( - audio_manager, this, input_parameters, device_id_); + audio_manager, this, input_parameters, device_id_, NULL); if (!audio_controller_.get()) { return Abort(SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO)); diff --git a/chromium/content/browser/ssl/ssl_manager.cc b/chromium/content/browser/ssl/ssl_manager.cc index c86dccdcda6..42ee02fbb1c 100644 --- a/chromium/content/browser/ssl/ssl_manager.cc +++ b/chromium/content/browser/ssl/ssl_manager.cc @@ -21,8 +21,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/load_from_memory_cache_details.h" #include "content/public/browser/navigation_details.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_source.h" #include "content/public/browser/resource_request_details.h" #include "content/public/common/ssl_status.h" #include "net/url_request/url_request.h" @@ -99,17 +97,6 @@ SSLManager::SSLManager(NavigationControllerImpl* controller) controller_(controller) { DCHECK(controller_); - // Subscribe to various notifications. - registrar_.Add( - this, NOTIFICATION_RESOURCE_RESPONSE_STARTED, - Source<WebContents>(controller_->web_contents())); - registrar_.Add( - this, NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, - Source<WebContents>(controller_->web_contents())); - registrar_.Add( - this, NOTIFICATION_LOAD_FROM_MEMORY_CACHE, - Source<NavigationController>(controller_)); - SSLManagerSet* managers = static_cast<SSLManagerSet*>( controller_->GetBrowserContext()->GetUserData(kSSLManagerKeyName)); if (!managers) { @@ -125,22 +112,18 @@ SSLManager::~SSLManager() { managers->get().erase(this); } -void SSLManager::DidCommitProvisionalLoad( - const NotificationDetails& in_details) { - LoadCommittedDetails* details = - Details<LoadCommittedDetails>(in_details).ptr(); - +void SSLManager::DidCommitProvisionalLoad(const LoadCommittedDetails& details) { NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(controller_->GetActiveEntry()); - if (details->is_main_frame) { + if (details.is_main_frame) { if (entry) { // Decode the security details. int ssl_cert_id; net::CertStatus ssl_cert_status; int ssl_security_bits; int ssl_connection_status; - DeserializeSecurityInfo(details->serialized_security_info, + DeserializeSecurityInfo(details.serialized_security_info, &ssl_cert_id, &ssl_cert_status, &ssl_security_bits, @@ -171,52 +154,32 @@ void SSLManager::DidRunInsecureContent(const std::string& security_origin) { UpdateEntry(navigation_entry); } -void SSLManager::Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) { - // Dispatch by type. - switch (type) { - case NOTIFICATION_RESOURCE_RESPONSE_STARTED: - DidStartResourceResponse( - Details<ResourceRequestDetails>(details).ptr()); - break; - case NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: - DidReceiveResourceRedirect( - Details<ResourceRedirectDetails>(details).ptr()); - break; - case NOTIFICATION_LOAD_FROM_MEMORY_CACHE: - DidLoadFromMemoryCache( - Details<LoadFromMemoryCacheDetails>(details).ptr()); - break; - default: - NOTREACHED() << "The SSLManager received an unexpected notification."; - } -} - -void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) { +void SSLManager::DidLoadFromMemoryCache( + const LoadFromMemoryCacheDetails& details) { // Simulate loading this resource through the usual path. // Note that we specify SUB_RESOURCE as the resource type as WebCore only // caches sub-resources. // This resource must have been loaded with no filtering because filtered // resouces aren't cachable. scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo( - details->url, + details.url, ResourceType::SUB_RESOURCE, - details->pid, - details->cert_id, - details->cert_status)); + details.pid, + details.cert_id, + details.cert_status)); // Simulate loading this resource through the usual path. policy()->OnRequestStarted(info.get()); } -void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) { +void SSLManager::DidStartResourceResponse( + const ResourceRequestDetails& details) { scoped_refptr<SSLRequestInfo> info(new SSLRequestInfo( - details->url, - details->resource_type, - details->origin_child_id, - details->ssl_cert_id, - details->ssl_cert_status)); + details.url, + details.resource_type, + details.origin_child_id, + details.ssl_cert_id, + details.ssl_cert_status)); // Notify our policy that we started a resource request. Ideally, the // policy should have the ability to cancel the request, but we can't do @@ -224,7 +187,8 @@ void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) { policy()->OnRequestStarted(info.get()); } -void SSLManager::DidReceiveResourceRedirect(ResourceRedirectDetails* details) { +void SSLManager::DidReceiveResourceRedirect( + const ResourceRedirectDetails& details) { // TODO(abarth): Make sure our redirect behavior is correct. If we ever see a // non-HTTPS resource in the redirect chain, we want to trigger // insecure content, even if the redirect chain goes back to diff --git a/chromium/content/browser/ssl/ssl_manager.h b/chromium/content/browser/ssl/ssl_manager.h index 6c80edd8c04..45100a7ea62 100644 --- a/chromium/content/browser/ssl/ssl_manager.h +++ b/chromium/content/browser/ssl/ssl_manager.h @@ -14,8 +14,6 @@ #include "content/browser/ssl/ssl_policy_backend.h" #include "content/common/content_export.h" #include "content/public/browser/global_request_id.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" #include "net/base/net_errors.h" #include "net/cert/cert_status_flags.h" #include "url/gurl.h" @@ -29,6 +27,7 @@ class BrowserContext; class NavigationEntryImpl; class NavigationControllerImpl; class SSLPolicy; +struct LoadCommittedDetails; struct LoadFromMemoryCacheDetails; struct ResourceRedirectDetails; struct ResourceRequestDetails; @@ -41,7 +40,7 @@ struct ResourceRequestDetails; // The security state (secure/insecure) is stored in the navigation entry. // Along with it are stored any SSL error code and the associated cert. -class SSLManager : public NotificationObserver { +class SSLManager { public: // Entry point for SSLCertificateErrors. This function begins the process // of resolving a certificate error during an SSL connection. SSLManager @@ -74,32 +73,16 @@ class SSLManager : public NotificationObserver { // NavigationController is guaranteed to outlive the SSLManager. NavigationControllerImpl* controller() { return controller_; } - // This entry point is called directly (instead of via the notification - // service) because we need more precise control of the order in which folks - // are notified of this event. - void DidCommitProvisionalLoad(const NotificationDetails& details); + void DidCommitProvisionalLoad(const LoadCommittedDetails& details); + void DidLoadFromMemoryCache(const LoadFromMemoryCacheDetails& details); + void DidStartResourceResponse(const ResourceRequestDetails& details); + void DidReceiveResourceRedirect(const ResourceRedirectDetails& details); // Insecure content entry point. void DidDisplayInsecureContent(); void DidRunInsecureContent(const std::string& security_origin); - // Entry point for navigation. This function begins the process of updating - // the security UI when the main frame navigates to a new URL. - // - // Called on the UI thread. - virtual void Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) OVERRIDE; - private: - // Entry points for notifications to which we subscribe. Note that - // DidCommitProvisionalLoad uses the abstract NotificationDetails type since - // the type we need is in NavigationController which would create a circular - // header file dependency. - void DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details); - void DidStartResourceResponse(ResourceRequestDetails* details); - void DidReceiveResourceRedirect(ResourceRedirectDetails* details); - // Update the NavigationEntry with our current state. void UpdateEntry(NavigationEntryImpl* entry); @@ -113,9 +96,6 @@ class SSLManager : public NotificationObserver { // for the security UI of this tab. NavigationControllerImpl* controller_; - // Handles registering notifications with the NotificationService. - NotificationRegistrar registrar_; - DISALLOW_COPY_AND_ASSIGN(SSLManager); }; diff --git a/chromium/content/browser/startup_task_runner.cc b/chromium/content/browser/startup_task_runner.cc index a7e730e5d87..4746afea6b0 100644 --- a/chromium/content/browser/startup_task_runner.cc +++ b/chromium/content/browser/startup_task_runner.cc @@ -11,53 +11,72 @@ namespace content { StartupTaskRunner::StartupTaskRunner( - bool browser_may_start_asynchronously, base::Callback<void(int)> const startup_complete_callback, scoped_refptr<base::SingleThreadTaskRunner> proxy) - : asynchronous_startup_(browser_may_start_asynchronously), - startup_complete_callback_(startup_complete_callback), - proxy_(proxy) {} + : startup_complete_callback_(startup_complete_callback), proxy_(proxy) {} + +StartupTaskRunner::~StartupTaskRunner() {} void StartupTaskRunner::AddTask(StartupTask& callback) { task_list_.push_back(callback); } -void StartupTaskRunner::StartRunningTasks() { +void StartupTaskRunner::StartRunningTasksAsync() { DCHECK(proxy_); int result = 0; - if (asynchronous_startup_ && !task_list_.empty()) { - const base::Closure next_task = - base::Bind(&StartupTaskRunner::WrappedTask, this); - proxy_->PostNonNestableTask(FROM_HERE, next_task); - } else { - for (std::list<StartupTask>::iterator it = task_list_.begin(); - it != task_list_.end(); - it++) { - result = it->Run(); - if (result > 0) { - break; - } - } + if (task_list_.empty()) { if (!startup_complete_callback_.is_null()) { startup_complete_callback_.Run(result); + // Clear the callback to prevent it being called a second time + startup_complete_callback_.Reset(); } + } else { + const base::Closure next_task = + base::Bind(&StartupTaskRunner::WrappedTask, base::Unretained(this)); + proxy_->PostNonNestableTask(FROM_HERE, next_task); + } +} + +void StartupTaskRunner::RunAllTasksNow() { + int result = 0; + for (std::list<StartupTask>::iterator it = task_list_.begin(); + it != task_list_.end(); + it++) { + result = it->Run(); + if (result > 0) break; + } + task_list_.clear(); + if (!startup_complete_callback_.is_null()) { + startup_complete_callback_.Run(result); + // Clear the callback to prevent it being called a second time + startup_complete_callback_.Reset(); } } void StartupTaskRunner::WrappedTask() { + if (task_list_.empty()) { + // This will happen if the remaining tasks have been run synchronously since + // the WrappedTask was created. Any callback will already have been called, + // so there is nothing to do + return; + } int result = task_list_.front().Run(); task_list_.pop_front(); - if (result > 0 || task_list_.empty()) { + if (result > 0) { + // Stop now and throw away the remaining tasks + task_list_.clear(); + } + if (task_list_.empty()) { if (!startup_complete_callback_.is_null()) { startup_complete_callback_.Run(result); + // Clear the callback to prevent it being called a second time + startup_complete_callback_.Reset(); } } else { const base::Closure next_task = - base::Bind(&StartupTaskRunner::WrappedTask, this); + base::Bind(&StartupTaskRunner::WrappedTask, base::Unretained(this)); proxy_->PostNonNestableTask(FROM_HERE, next_task); } } -StartupTaskRunner::~StartupTaskRunner() {} - } // namespace content diff --git a/chromium/content/browser/startup_task_runner.h b/chromium/content/browser/startup_task_runner.h index 5f954edc887..80e5627803f 100644 --- a/chromium/content/browser/startup_task_runner.h +++ b/chromium/content/browser/startup_task_runner.h @@ -8,7 +8,6 @@ #include <list> #include "base/callback.h" -#include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" #include "build/build_config.h" @@ -32,30 +31,31 @@ typedef base::Callback<int(void)> StartupTask; // no opportunity to handle UI events between the tasks of a // SingleThreadedTaskRunner. -class CONTENT_EXPORT StartupTaskRunner - : public base::RefCounted<StartupTaskRunner> { +class CONTENT_EXPORT StartupTaskRunner { public: // Constructor: Note that |startup_complete_callback| is optional. If it is // not null it will be called once all the startup tasks have run. - StartupTaskRunner(bool browser_may_start_asynchronously, - base::Callback<void(int)> startup_complete_callback, + StartupTaskRunner(base::Callback<void(int)> startup_complete_callback, scoped_refptr<base::SingleThreadTaskRunner> proxy); + ~StartupTaskRunner(); + // Add a task to the queue of startup tasks to be run. - virtual void AddTask(StartupTask& callback); + void AddTask(StartupTask& callback); + + // Start running the tasks asynchronously. + void StartRunningTasksAsync(); - // Start running the tasks. - virtual void StartRunningTasks(); + // Run all tasks, or all remaining tasks, synchronously + void RunAllTasksNow(); private: friend class base::RefCounted<StartupTaskRunner>; - virtual ~StartupTaskRunner(); std::list<StartupTask> task_list_; void WrappedTask(); - const bool asynchronous_startup_; base::Callback<void(int)> startup_complete_callback_; scoped_refptr<base::SingleThreadTaskRunner> proxy_; diff --git a/chromium/content/browser/startup_task_runner_unittest.cc b/chromium/content/browser/startup_task_runner_unittest.cc index 2efa79f7ac6..fcd2b3d1da6 100644 --- a/chromium/content/browser/startup_task_runner_unittest.cc +++ b/chromium/content/browser/startup_task_runner_unittest.cc @@ -23,7 +23,8 @@ using testing::Assign; using testing::Invoke; using testing::WithArg; -bool observer_called = false; +int observer_calls = 0; +int task_count = 0; int observer_result; base::Closure task; @@ -35,7 +36,7 @@ bool SaveTaskArg(const Closure& arg) { } void Observer(int result) { - observer_called = true; + observer_calls++; observer_result = result; } @@ -44,22 +45,26 @@ class StartupTaskRunnerTest : public testing::Test { virtual void SetUp() { last_task_ = 0; - observer_called = false; + observer_calls = 0; + task_count = 0; } int Task1() { last_task_ = 1; + task_count++; return 0; } int Task2() { last_task_ = 2; + task_count++; return 0; } int FailingTask() { // Task returning failure last_task_ = 3; + task_count++; return 1; } @@ -116,26 +121,35 @@ TEST_F(StartupTaskRunnerTest, SynchronousExecution) { EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0); EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0); - scoped_refptr<StartupTaskRunner> runner = - new StartupTaskRunner(false, base::Bind(&Observer), proxy); + StartupTaskRunner runner(base::Bind(&Observer), proxy); StartupTask task1 = base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this)); - runner->AddTask(task1); + runner.AddTask(task1); EXPECT_EQ(GetLastTask(), 0); StartupTask task2 = base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this)); - runner->AddTask(task2); + runner.AddTask(task2); // Nothing should run until we tell them to. EXPECT_EQ(GetLastTask(), 0); - runner->StartRunningTasks(); + runner.RunAllTasksNow(); // On an immediate StartupTaskRunner the tasks should now all have run. EXPECT_EQ(GetLastTask(), 2); - EXPECT_TRUE(observer_called); + EXPECT_EQ(task_count, 2); + EXPECT_EQ(observer_calls, 1); EXPECT_EQ(observer_result, 0); + + // Running the tasks asynchronously shouldn't do anything + // In particular Post... should not be called + runner.StartRunningTasksAsync(); + + // No more tasks should be run and the observer should not have been called + // again + EXPECT_EQ(task_count, 2); + EXPECT_EQ(observer_calls, 1); } TEST_F(StartupTaskRunnerTest, NullObserver) { @@ -145,25 +159,32 @@ TEST_F(StartupTaskRunnerTest, NullObserver) { EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0); EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0); - scoped_refptr<StartupTaskRunner> runner = - new StartupTaskRunner(false, base::Callback<void(int)>(), proxy); + StartupTaskRunner runner(base::Callback<void(int)>(), proxy); StartupTask task1 = base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this)); - runner->AddTask(task1); + runner.AddTask(task1); EXPECT_EQ(GetLastTask(), 0); StartupTask task2 = base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this)); - runner->AddTask(task2); + runner.AddTask(task2); // Nothing should run until we tell them to. EXPECT_EQ(GetLastTask(), 0); - runner->StartRunningTasks(); + runner.RunAllTasksNow(); // On an immediate StartupTaskRunner the tasks should now all have run. EXPECT_EQ(GetLastTask(), 2); + EXPECT_EQ(task_count, 2); + + // Running the tasks asynchronously shouldn't do anything + // In particular Post... should not be called + runner.StartRunningTasksAsync(); - EXPECT_FALSE(observer_called); + // No more tasks should have been run + EXPECT_EQ(task_count, 2); + + EXPECT_EQ(observer_calls, 0); } TEST_F(StartupTaskRunnerTest, SynchronousExecutionFailedTask) { @@ -173,26 +194,34 @@ TEST_F(StartupTaskRunnerTest, SynchronousExecutionFailedTask) { EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0); EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0); - scoped_refptr<StartupTaskRunner> runner = - new StartupTaskRunner(false, base::Bind(&Observer), proxy); + StartupTaskRunner runner(base::Bind(&Observer), proxy); StartupTask task3 = base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this)); - runner->AddTask(task3); + runner.AddTask(task3); EXPECT_EQ(GetLastTask(), 0); StartupTask task2 = base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this)); - runner->AddTask(task2); + runner.AddTask(task2); // Nothing should run until we tell them to. EXPECT_EQ(GetLastTask(), 0); - runner->StartRunningTasks(); + runner.RunAllTasksNow(); // Only the first task should have run, since it failed EXPECT_EQ(GetLastTask(), 3); - - EXPECT_TRUE(observer_called); + EXPECT_EQ(task_count, 1); + EXPECT_EQ(observer_calls, 1); EXPECT_EQ(observer_result, 1); + + // After a failed task all remaining tasks should be cancelled + // In particular Post... should not be called by running asynchronously + runner.StartRunningTasksAsync(); + + // The observer should only be called the first time the queue completes and + // no more tasks should have run + EXPECT_EQ(observer_calls, 1); + EXPECT_EQ(task_count, 1); } TEST_F(StartupTaskRunnerTest, AsynchronousExecution) { @@ -207,19 +236,18 @@ TEST_F(StartupTaskRunnerTest, AsynchronousExecution) { .Times(testing::Between(2, 3)) .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg))); - scoped_refptr<StartupTaskRunner> runner = - new StartupTaskRunner(true, base::Bind(&Observer), proxy); + StartupTaskRunner runner(base::Bind(&Observer), proxy); StartupTask task1 = base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this)); - runner->AddTask(task1); + runner.AddTask(task1); StartupTask task2 = base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this)); - runner->AddTask(task2); + runner.AddTask(task2); // Nothing should run until we tell them to. EXPECT_EQ(GetLastTask(), 0); - runner->StartRunningTasks(); + runner.StartRunningTasksAsync(); // No tasks should have run yet, since we the message loop hasn't run. EXPECT_EQ(GetLastTask(), 0); @@ -228,12 +256,19 @@ TEST_F(StartupTaskRunnerTest, AsynchronousExecution) { // be added to the queue, hence updating "task". The loop should actually run // at most 3 times (once for each task plus possibly once for the observer), // the "4" is a backstop. - for (int i = 0; i < 4 && !observer_called; i++) { + for (int i = 0; i < 4 && observer_calls == 0; i++) { task.Run(); EXPECT_EQ(i + 1, GetLastTask()); } - EXPECT_TRUE(observer_called); + EXPECT_EQ(task_count, 2); + EXPECT_EQ(observer_calls, 1); EXPECT_EQ(observer_result, 0); + + // Check that running synchronously now doesn't do anything + + runner.RunAllTasksNow(); + EXPECT_EQ(task_count, 2); + EXPECT_EQ(observer_calls, 1); } TEST_F(StartupTaskRunnerTest, AsynchronousExecutionFailedTask) { @@ -248,19 +283,18 @@ TEST_F(StartupTaskRunnerTest, AsynchronousExecutionFailedTask) { .Times(testing::Between(1, 2)) .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg))); - scoped_refptr<StartupTaskRunner> runner = - new StartupTaskRunner(true, base::Bind(&Observer), proxy); + StartupTaskRunner runner(base::Bind(&Observer), proxy); StartupTask task3 = base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this)); - runner->AddTask(task3); + runner.AddTask(task3); StartupTask task2 = base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this)); - runner->AddTask(task2); + runner.AddTask(task2); // Nothing should run until we tell them to. EXPECT_EQ(GetLastTask(), 0); - runner->StartRunningTasks(); + runner.StartRunningTasksAsync(); // No tasks should have run yet, since we the message loop hasn't run. EXPECT_EQ(GetLastTask(), 0); @@ -269,13 +303,19 @@ TEST_F(StartupTaskRunnerTest, AsynchronousExecutionFailedTask) { // be added to the queue, hence updating "task". The loop should actually run // at most twice (once for the failed task plus possibly once for the // observer), the "4" is a backstop. - for (int i = 0; i < 4 && !observer_called; i++) { + for (int i = 0; i < 4 && observer_calls == 0; i++) { task.Run(); } EXPECT_EQ(GetLastTask(), 3); + EXPECT_EQ(task_count, 1); - EXPECT_TRUE(observer_called); + EXPECT_EQ(observer_calls, 1); EXPECT_EQ(observer_result, 1); + + // Check that running synchronously now doesn't do anything + runner.RunAllTasksNow(); + EXPECT_EQ(observer_calls, 1); + EXPECT_EQ(task_count, 1); } } // namespace } // namespace content diff --git a/chromium/content/browser/storage_partition_impl.cc b/chromium/content/browser/storage_partition_impl.cc index c5471869150..e984e617343 100644 --- a/chromium/content/browser/storage_partition_impl.cc +++ b/chromium/content/browser/storage_partition_impl.cc @@ -112,7 +112,6 @@ void ClearQuotaManagedOriginsOnIOThread(quota::QuotaManager* quota_manager, return; } - std::set<GURL>::const_iterator origin; size_t* origins_to_delete_count = new size_t(origins.size()); for (std::set<GURL>::const_iterator origin = origins.begin(); origin != origins.end(); ++origin) { @@ -285,6 +284,7 @@ StoragePartitionImpl::StoragePartitionImpl( webkit_database::DatabaseTracker* database_tracker, DOMStorageContextWrapper* dom_storage_context, IndexedDBContextImpl* indexed_db_context, + ServiceWorkerContext* service_worker_context, WebRTCIdentityStore* webrtc_identity_store) : partition_path_(partition_path), quota_manager_(quota_manager), @@ -293,6 +293,7 @@ StoragePartitionImpl::StoragePartitionImpl( database_tracker_(database_tracker), dom_storage_context_(dom_storage_context), indexed_db_context_(indexed_db_context), + service_worker_context_(service_worker_context), webrtc_identity_store_(webrtc_identity_store) {} StoragePartitionImpl::~StoragePartitionImpl() { @@ -368,6 +369,9 @@ StoragePartitionImpl* StoragePartitionImpl::Create( quota_manager->proxy(), idb_task_runner); + scoped_refptr<ServiceWorkerContext> service_worker_context = + new ServiceWorkerContext(path, quota_manager->proxy()); + scoped_refptr<ChromeAppCacheService> appcache_service = new ChromeAppCacheService(quota_manager->proxy()); @@ -381,6 +385,7 @@ StoragePartitionImpl* StoragePartitionImpl::Create( database_tracker.get(), dom_storage_context.get(), indexed_db_context.get(), + service_worker_context.get(), webrtc_identity_store.get()); } @@ -421,6 +426,10 @@ IndexedDBContextImpl* StoragePartitionImpl::GetIndexedDBContext() { return indexed_db_context_.get(); } +ServiceWorkerContext* StoragePartitionImpl::GetServiceWorkerContext() { + return service_worker_context_.get(); +} + void StoragePartitionImpl::ClearDataImpl( uint32 remove_mask, uint32 quota_storage_remove_mask, diff --git a/chromium/content/browser/storage_partition_impl.h b/chromium/content/browser/storage_partition_impl.h index e59347cb569..87130e10bd8 100644 --- a/chromium/content/browser/storage_partition_impl.h +++ b/chromium/content/browser/storage_partition_impl.h @@ -12,6 +12,7 @@ #include "content/browser/dom_storage/dom_storage_context_wrapper.h" #include "content/browser/indexed_db/indexed_db_context_impl.h" #include "content/browser/media/webrtc_identity_store.h" +#include "content/browser/service_worker/service_worker_context.h" #include "content/common/content_export.h" #include "content/public/browser/storage_partition.h" @@ -46,6 +47,8 @@ class StoragePartitionImpl : public StoragePartition { const base::Time& end, const base::Closure& callback) OVERRIDE; + ServiceWorkerContext* GetServiceWorkerContext(); + WebRTCIdentityStore* GetWebRTCIdentityStore(); struct DataDeletionHelper; @@ -77,6 +80,7 @@ class StoragePartitionImpl : public StoragePartition { webkit_database::DatabaseTracker* database_tracker, DOMStorageContextWrapper* dom_storage_context, IndexedDBContextImpl* indexed_db_context, + ServiceWorkerContext* service_worker_context, WebRTCIdentityStore* webrtc_identity_store); void ClearDataImpl(uint32 remove_mask, @@ -112,6 +116,7 @@ class StoragePartitionImpl : public StoragePartition { scoped_refptr<webkit_database::DatabaseTracker> database_tracker_; scoped_refptr<DOMStorageContextWrapper> dom_storage_context_; scoped_refptr<IndexedDBContextImpl> indexed_db_context_; + scoped_refptr<ServiceWorkerContext> service_worker_context_; scoped_refptr<WebRTCIdentityStore> webrtc_identity_store_; DISALLOW_COPY_AND_ASSIGN(StoragePartitionImpl); diff --git a/chromium/content/browser/storage_partition_impl_map.cc b/chromium/content/browser/storage_partition_impl_map.cc index c5721aee667..feb9515fe67 100644 --- a/chromium/content/browser/storage_partition_impl_map.cc +++ b/chromium/content/browser/storage_partition_impl_map.cc @@ -34,18 +34,20 @@ #include "crypto/sha2.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +#include "webkit/browser/blob/blob_storage_context.h" #include "webkit/browser/blob/blob_url_request_job_factory.h" #include "webkit/browser/fileapi/file_system_url_request_job_factory.h" #include "webkit/common/blob/blob_data.h" using appcache::AppCacheService; using fileapi::FileSystemContext; -using webkit_blob::BlobStorageController; +using webkit_blob::BlobStorageContext; namespace content { namespace { +// A derivative that knows about Streams too. class BlobProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler { public: BlobProtocolHandler(ChromeBlobStorageContext* blob_storage_context, @@ -53,77 +55,39 @@ class BlobProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler { fileapi::FileSystemContext* file_system_context) : blob_storage_context_(blob_storage_context), stream_context_(stream_context), - file_system_context_(file_system_context) {} + file_system_context_(file_system_context) { + } - virtual ~BlobProtocolHandler() {} + virtual ~BlobProtocolHandler() { + } virtual net::URLRequestJob* MaybeCreateJob( net::URLRequest* request, net::NetworkDelegate* network_delegate) const OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!webkit_blob_protocol_handler_impl_) { - webkit_blob_protocol_handler_impl_.reset( - new WebKitBlobProtocolHandlerImpl(blob_storage_context_->controller(), - stream_context_.get(), - file_system_context_.get())); + scoped_refptr<Stream> stream = + stream_context_->registry()->GetStream(request->url()); + if (stream.get()) + return new StreamURLRequestJob(request, network_delegate, stream); + + if (!blob_protocol_handler_) { + // Construction is deferred because 'this' is constructed on + // the main thread but we want blob_protocol_handler_ constructed + // on the IO thread. + blob_protocol_handler_.reset( + new webkit_blob::BlobProtocolHandler( + blob_storage_context_->context(), + file_system_context_, + BrowserThread::GetMessageLoopProxyForThread( + BrowserThread::FILE).get())); } - return webkit_blob_protocol_handler_impl_->MaybeCreateJob(request, - network_delegate); + return blob_protocol_handler_->MaybeCreateJob(request, network_delegate); } private: - // An implementation of webkit_blob::BlobProtocolHandler that gets - // the BlobData from ResourceRequestInfoImpl. - class WebKitBlobProtocolHandlerImpl - : public webkit_blob::BlobProtocolHandler { - public: - WebKitBlobProtocolHandlerImpl( - webkit_blob::BlobStorageController* blob_storage_controller, - StreamContext* stream_context, - fileapi::FileSystemContext* file_system_context) - : webkit_blob::BlobProtocolHandler( - blob_storage_controller, - file_system_context, - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE) - .get()), - stream_context_(stream_context) {} - - virtual ~WebKitBlobProtocolHandlerImpl() {} - - virtual net::URLRequestJob* MaybeCreateJob( - net::URLRequest* request, - net::NetworkDelegate* network_delegate) const OVERRIDE { - scoped_refptr<Stream> stream = - stream_context_->registry()->GetStream(request->url()); - if (stream.get()) - return new StreamURLRequestJob(request, network_delegate, stream); - - return webkit_blob::BlobProtocolHandler::MaybeCreateJob( - request, network_delegate); - } - - private: - // webkit_blob::BlobProtocolHandler implementation. - virtual scoped_refptr<webkit_blob::BlobData> - LookupBlobData(net::URLRequest* request) const OVERRIDE { - const ResourceRequestInfoImpl* info = - ResourceRequestInfoImpl::ForRequest(request); - if (!info) - return NULL; - return info->requested_blob_data(); - } - - const scoped_refptr<StreamContext> stream_context_; - DISALLOW_COPY_AND_ASSIGN(WebKitBlobProtocolHandlerImpl); - }; - const scoped_refptr<ChromeBlobStorageContext> blob_storage_context_; const scoped_refptr<StreamContext> stream_context_; const scoped_refptr<fileapi::FileSystemContext> file_system_context_; - - mutable scoped_ptr<WebKitBlobProtocolHandlerImpl> - webkit_blob_protocol_handler_impl_; - + mutable scoped_ptr<webkit_blob::BlobProtocolHandler> blob_protocol_handler_; DISALLOW_COPY_AND_ASSIGN(BlobProtocolHandler); }; diff --git a/chromium/content/browser/storage_partition_impl_unittest.cc b/chromium/content/browser/storage_partition_impl_unittest.cc index a8b47bc74ca..4828ae6529f 100644 --- a/chromium/content/browser/storage_partition_impl_unittest.cc +++ b/chromium/content/browser/storage_partition_impl_unittest.cc @@ -110,7 +110,7 @@ TEST_F(StoragePartitionShaderClearTest, ClearShaderCache) { TestClosureCallback clear_cb; StoragePartitionImpl sp( - cache_path(), NULL, NULL, NULL, NULL, NULL, NULL, NULL); + cache_path(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ClearData, &sp, clear_cb.callback())); clear_cb.WaitForResult(); diff --git a/chromium/content/browser/streams/stream.cc b/chromium/content/browser/streams/stream.cc index f5abe0276e8..5d20fe6ab64 100644 --- a/chromium/content/browser/streams/stream.cc +++ b/chromium/content/browser/streams/stream.cc @@ -23,10 +23,11 @@ namespace content { Stream::Stream(StreamRegistry* registry, StreamWriteObserver* write_observer, const GURL& url) - : data_bytes_read_(0), - can_add_data_(true), + : can_add_data_(true), url_(url), data_length_(0), + data_bytes_read_(0), + last_total_buffered_bytes_(0), registry_(registry), read_observer_(NULL), write_observer_(write_observer), @@ -67,19 +68,50 @@ void Stream::RemoveWriteObserver(StreamWriteObserver* observer) { write_observer_ = NULL; } +void Stream::Abort() { + // Clear all buffer. It's safe to clear reader_ here since the same thread + // is used for both input and output operation. + writer_.reset(); + reader_.reset(); + ClearBuffer(); + can_add_data_ = false; + registry_->UnregisterStream(url()); +} + void Stream::AddData(scoped_refptr<net::IOBuffer> buffer, size_t size) { + if (!writer_.get()) + return; + + size_t current_buffered_bytes = writer_->GetTotalBufferedBytes(); + if (!registry_->UpdateMemoryUsage(url(), current_buffered_bytes, size)) { + Abort(); + return; + } + + // Now it's guaranteed that this doesn't overflow. This must be done before + // Write() since GetTotalBufferedBytes() may return different value after + // Write() call, so if we use the new value, information in this instance and + // one in |registry_| become inconsistent. + last_total_buffered_bytes_ = current_buffered_bytes + size; + can_add_data_ = writer_->Write(buffer, size); } void Stream::AddData(const char* data, size_t size) { + if (!writer_.get()) + return; + scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(size)); memcpy(io_buffer->data(), data, size); - can_add_data_ = writer_->Write(io_buffer, size); + AddData(io_buffer, size); } void Stream::Finalize() { + if (!writer_.get()) + return; + writer_->Close(0); - writer_.reset(NULL); + writer_.reset(); // Continue asynchronously. base::MessageLoopProxy::current()->PostTask( @@ -90,10 +122,17 @@ void Stream::Finalize() { Stream::StreamState Stream::ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read) { + DCHECK(buf); + DCHECK(bytes_read); + *bytes_read = 0; if (!data_.get()) { - data_length_ = 0; - data_bytes_read_ = 0; + DCHECK(!data_length_); + DCHECK(!data_bytes_read_); + + if (!reader_.get()) + return STREAM_ABORTED; + ByteStreamReader::StreamState state = reader_->Read(&data_, &data_length_); switch (state) { case ByteStreamReader::STREAM_HAS_DATA: @@ -113,7 +152,7 @@ Stream::StreamState Stream::ReadRawData(net::IOBuffer* buf, memcpy(buf->data(), data_->data() + data_bytes_read_, to_read); data_bytes_read_ += to_read; if (data_bytes_read_ >= data_length_) - data_ = NULL; + ClearBuffer(); *bytes_read = to_read; return STREAM_HAS_DATA; @@ -150,4 +189,10 @@ void Stream::OnDataAvailable() { read_observer_->OnDataAvailable(this); } +void Stream::ClearBuffer() { + data_ = NULL; + data_length_ = 0; + data_bytes_read_ = 0; +} + } // namespace content diff --git a/chromium/content/browser/streams/stream.h b/chromium/content/browser/streams/stream.h index 85edc884081..3937f5b9e38 100644 --- a/chromium/content/browser/streams/stream.h +++ b/chromium/content/browser/streams/stream.h @@ -36,6 +36,7 @@ class CONTENT_EXPORT Stream : public base::RefCountedThreadSafe<Stream> { STREAM_HAS_DATA, STREAM_COMPLETE, STREAM_EMPTY, + STREAM_ABORTED, }; // Creates a stream. @@ -57,6 +58,10 @@ class CONTENT_EXPORT Stream : public base::RefCountedThreadSafe<Stream> { // Removes the write observer. |observer| must be the current observer. void RemoveWriteObserver(StreamWriteObserver* observer); + // Stops accepting new data, clears all buffer, unregisters this stream from + // |registry_| and make coming ReadRawData() calls return STREAM_ABORTED. + void Abort(); + // Adds the data in |buffer| to the stream. Takes ownership of |buffer|. void AddData(scoped_refptr<net::IOBuffer> buffer, size_t size); // Adds data of |size| at |data| to the stream. This method creates a copy @@ -81,6 +86,11 @@ class CONTENT_EXPORT Stream : public base::RefCountedThreadSafe<Stream> { const GURL& url() const { return url_; } + // For StreamRegistry to remember the last memory usage reported to it. + size_t last_total_buffered_bytes() const { + return last_total_buffered_bytes_; + } + private: friend class base::RefCountedThreadSafe<Stream>; @@ -89,13 +99,25 @@ class CONTENT_EXPORT Stream : public base::RefCountedThreadSafe<Stream> { void OnSpaceAvailable(); void OnDataAvailable(); - size_t data_bytes_read_; + // Clears |data_| and related variables. + void ClearBuffer(); + bool can_add_data_; GURL url_; + // Buffer for storing data read from |reader_| but not yet read out from this + // Stream by ReadRawData() method. scoped_refptr<net::IOBuffer> data_; + // Number of bytes read from |reader_| into |data_| including bytes already + // read out. size_t data_length_; + // Number of bytes in |data_| that are already read out. + size_t data_bytes_read_; + + // Last value returned by writer_->TotalBufferedBytes() in AddData(). Stored + // in order to check memory usage. + size_t last_total_buffered_bytes_; scoped_ptr<ByteStreamWriter> writer_; scoped_ptr<ByteStreamReader> reader_; diff --git a/chromium/content/browser/streams/stream_registry.cc b/chromium/content/browser/streams/stream_registry.cc index 39d24b39f5f..f722f57d03c 100644 --- a/chromium/content/browser/streams/stream_registry.cc +++ b/chromium/content/browser/streams/stream_registry.cc @@ -8,7 +8,15 @@ namespace content { -StreamRegistry::StreamRegistry() { +namespace { +// The maximum size of memory each StreamRegistry instance is allowed to use +// for its Stream instances. +const size_t kDefaultMaxMemoryUsage = 1024 * 1024 * 1024U; // 1GiB +} + +StreamRegistry::StreamRegistry() + : total_memory_usage_(0), + max_memory_usage_(kDefaultMaxMemoryUsage) { } StreamRegistry::~StreamRegistry() { @@ -42,7 +50,43 @@ bool StreamRegistry::CloneStream(const GURL& url, const GURL& src_url) { void StreamRegistry::UnregisterStream(const GURL& url) { DCHECK(CalledOnValidThread()); + + StreamMap::iterator iter = streams_.find(url); + if (iter == streams_.end()) + return; + + // Only update |total_memory_usage_| if |url| is NOT a Stream clone because + // cloned streams do not update |total_memory_usage_|. + if (iter->second->url() == url) { + size_t buffered_bytes = iter->second->last_total_buffered_bytes(); + DCHECK_LE(buffered_bytes, total_memory_usage_); + total_memory_usage_ -= buffered_bytes; + } + streams_.erase(url); } +bool StreamRegistry::UpdateMemoryUsage(const GURL& url, + size_t current_size, + size_t increase) { + DCHECK(CalledOnValidThread()); + + StreamMap::iterator iter = streams_.find(url); + // A Stream must be registered with its parent registry to get memory. + if (iter == streams_.end()) + return false; + + size_t last_size = iter->second->last_total_buffered_bytes(); + DCHECK_LE(last_size, total_memory_usage_); + size_t usage_of_others = total_memory_usage_ - last_size; + DCHECK_LE(current_size, last_size); + size_t current_total_memory_usage = usage_of_others + current_size; + + if (increase > max_memory_usage_ - current_total_memory_usage) + return false; + + total_memory_usage_ = current_total_memory_usage + increase; + return true; +} + } // namespace content diff --git a/chromium/content/browser/streams/stream_registry.h b/chromium/content/browser/streams/stream_registry.h index e75c97c1699..a359411d2ae 100644 --- a/chromium/content/browser/streams/stream_registry.h +++ b/chromium/content/browser/streams/stream_registry.h @@ -32,20 +32,37 @@ class CONTENT_EXPORT StreamRegistry : public base::NonThreadSafe { void UnregisterStream(const GURL& url); + // Called by Stream instances to request increase of memory usage. If the + // total memory usage for this registry is going to exceed the limit, + // returns false. Otherwise, updates |total_memory_usage_| and returns true. + // + // |current_size| is the up-to-date size of ByteStream of the Stream instance + // and |increase| must be the amount of data going to be added to the Stream + // instance. + bool UpdateMemoryUsage(const GURL& url, size_t current_size, size_t increase); + // Gets the stream associated with |url|. Returns NULL if there is no such // stream. scoped_refptr<Stream> GetStream(const GURL& url); + void set_max_memory_usage_for_testing(size_t size) { + max_memory_usage_ = size; + } + private: typedef std::map<GURL, scoped_refptr<Stream> > StreamMap; StreamMap streams_; + size_t total_memory_usage_; + + // Maximum amount of memory allowed to use for Stream instances registered + // with this registry. + size_t max_memory_usage_; + DISALLOW_COPY_AND_ASSIGN(StreamRegistry); }; } // namespace content #endif // CONTENT_BROWSER_STREAMS_STREAM_REGISTRY_H_ - - diff --git a/chromium/content/browser/streams/stream_unittest.cc b/chromium/content/browser/streams/stream_unittest.cc index 36add1d649d..a2d959305fa 100644 --- a/chromium/content/browser/streams/stream_unittest.cc +++ b/chromium/content/browser/streams/stream_unittest.cc @@ -41,7 +41,7 @@ class StreamTest : public testing::Test { class TestStreamReader : public StreamReadObserver { public: - TestStreamReader() : buffer_(new net::GrowableIOBuffer()) { + TestStreamReader() : buffer_(new net::GrowableIOBuffer()), completed_(false) { } virtual ~TestStreamReader() {} @@ -50,8 +50,25 @@ class TestStreamReader : public StreamReadObserver { scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize)); int bytes_read = 0; - while (stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read) == - Stream::STREAM_HAS_DATA) { + while (true) { + Stream::StreamState state = + stream->ReadRawData(buffer.get(), kBufferSize, &bytes_read); + switch (state) { + case Stream::STREAM_HAS_DATA: + // TODO(tyoshino): Move these expectations to the beginning of Read() + // method once Stream::Finalize() is fixed. + EXPECT_FALSE(completed_); + break; + case Stream::STREAM_COMPLETE: + completed_ = true; + return; + case Stream::STREAM_EMPTY: + EXPECT_FALSE(completed_); + return; + case Stream::STREAM_ABORTED: + EXPECT_FALSE(completed_); + return; + } size_t old_capacity = buffer_->capacity(); buffer_->SetCapacity(old_capacity + bytes_read); memcpy(buffer_->StartOfBuffer() + old_capacity, @@ -65,8 +82,13 @@ class TestStreamReader : public StreamReadObserver { scoped_refptr<net::GrowableIOBuffer> buffer() { return buffer_; } + bool completed() const { + return completed_; + } + private: scoped_refptr<net::GrowableIOBuffer> buffer_; + bool completed_; }; class TestStreamWriter : public StreamWriteObserver { @@ -137,14 +159,38 @@ TEST_F(StreamTest, Stream) { scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize)); writer.Write(stream.get(), buffer, kBufferSize); stream->Finalize(); - reader.Read(stream.get()); base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(reader.completed()); ASSERT_EQ(reader.buffer()->capacity(), kBufferSize); for (int i = 0; i < kBufferSize; i++) EXPECT_EQ(buffer->data()[i], reader.buffer()->data()[i]); } +// Test that even if a reader receives an empty buffer, once TransferData() +// method is called on it with |source_complete| = true, following Read() calls +// on it never returns STREAM_EMPTY. Together with StreamTest.Stream above, this +// guarantees that Reader::Read() call returns only STREAM_HAS_DATA +// or STREAM_COMPLETE in |data_available_callback_| call corresponding to +// Writer::Close(). +TEST_F(StreamTest, ClosedReaderDoesNotReturnStreamEmpty) { + TestStreamReader reader; + TestStreamWriter writer; + + GURL url("blob://stream"); + scoped_refptr<Stream> stream( + new Stream(registry_.get(), &writer, url)); + EXPECT_TRUE(stream->SetReadObserver(&reader)); + + const int kBufferSize = 0; + scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize)); + stream->AddData(buffer, kBufferSize); + stream->Finalize(); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(reader.completed()); + EXPECT_EQ(0, reader.buffer()->capacity()); +} + TEST_F(StreamTest, GetStream) { TestStreamWriter writer; @@ -207,4 +253,59 @@ TEST_F(StreamTest, UnregisterStream) { ASSERT_FALSE(stream2.get()); } +TEST_F(StreamTest, MemoryExceedMemoryUsageLimit) { + TestStreamWriter writer1; + TestStreamWriter writer2; + + GURL url1("blob://stream"); + scoped_refptr<Stream> stream1( + new Stream(registry_.get(), &writer1, url1)); + + GURL url2("blob://stream2"); + scoped_refptr<Stream> stream2( + new Stream(registry_.get(), &writer2, url2)); + + const int kMaxMemoryUsage = 1500000; + registry_->set_max_memory_usage_for_testing(kMaxMemoryUsage); + + const int kBufferSize = 1000000; + scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize)); + writer1.Write(stream1.get(), buffer, kBufferSize); + // Make transfer happen. + base::MessageLoop::current()->RunUntilIdle(); + + writer2.Write(stream2.get(), buffer, kBufferSize); + + // Written data (1000000 * 2) exceeded limit (1500000). |stream2| should be + // unregistered with |registry_|. + EXPECT_EQ(NULL, registry_->GetStream(url2).get()); + + writer1.Write(stream1.get(), buffer, kMaxMemoryUsage - kBufferSize); + // Should be accepted since stream2 is unregistered and the new data is not + // so big to exceed the limit. + EXPECT_FALSE(registry_->GetStream(url1).get() == NULL); +} + +TEST_F(StreamTest, UnderMemoryUsageLimit) { + TestStreamWriter writer; + TestStreamReader reader; + + GURL url("blob://stream"); + scoped_refptr<Stream> stream(new Stream(registry_.get(), &writer, url)); + EXPECT_TRUE(stream->SetReadObserver(&reader)); + + registry_->set_max_memory_usage_for_testing(1500000); + + const int kBufferSize = 1000000; + scoped_refptr<net::IOBuffer> buffer(NewIOBuffer(kBufferSize)); + writer.Write(stream.get(), buffer, kBufferSize); + + // Run loop to make |reader| consume the data. + base::MessageLoop::current()->RunUntilIdle(); + + writer.Write(stream.get(), buffer, kBufferSize); + + EXPECT_EQ(stream.get(), registry_->GetStream(url).get()); +} + } // namespace content diff --git a/chromium/content/browser/streams/stream_url_request_job.cc b/chromium/content/browser/streams/stream_url_request_job.cc index e36c5d49ae6..09b9e6d7815 100644 --- a/chromium/content/browser/streams/stream_url_request_job.cc +++ b/chromium/content/browser/streams/stream_url_request_job.cc @@ -39,19 +39,42 @@ StreamURLRequestJob::~StreamURLRequestJob() { void StreamURLRequestJob::OnDataAvailable(Stream* stream) { // Clear the IO_PENDING status. SetStatus(net::URLRequestStatus()); - if (pending_buffer_.get()) { - int bytes_read; - stream_->ReadRawData( - pending_buffer_.get(), pending_buffer_size_, &bytes_read); - - // Clear the buffers before notifying the read is complete, so that it is - // safe for the observer to read. - pending_buffer_ = NULL; - pending_buffer_size_ = 0; - - total_bytes_read_ += bytes_read; - NotifyReadComplete(bytes_read); + // Do nothing if pending_buffer_ is empty, i.e. there's no ReadRawData() + // operation waiting for IO completion. + if (!pending_buffer_.get()) + return; + + // pending_buffer_ is set to the IOBuffer instance provided to ReadRawData() + // by URLRequestJob. + + int bytes_read; + switch (stream_->ReadRawData( + pending_buffer_.get(), pending_buffer_size_, &bytes_read)) { + case Stream::STREAM_HAS_DATA: + DCHECK_GT(bytes_read, 0); + break; + case Stream::STREAM_COMPLETE: + // Ensure this. Calling NotifyReadComplete call with 0 signals + // completion. + bytes_read = 0; + break; + case Stream::STREAM_EMPTY: + NOTREACHED(); + break; + case Stream::STREAM_ABORTED: + // Handle this as connection reset. + NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_CONNECTION_RESET)); + break; } + + // Clear the buffers before notifying the read is complete, so that it is + // safe for the observer to read. + pending_buffer_ = NULL; + pending_buffer_size_ = 0; + + total_bytes_read_ += bytes_read; + NotifyReadComplete(bytes_read); } // net::URLRequestJob methods. @@ -74,6 +97,7 @@ bool StreamURLRequestJob::ReadRawData(net::IOBuffer* buf, if (request_failed_) return true; + DCHECK(buf); DCHECK(bytes_read); int to_read = buf_size; if (max_range_ && to_read) { @@ -96,6 +120,11 @@ bool StreamURLRequestJob::ReadRawData(net::IOBuffer* buf, pending_buffer_size_ = to_read; SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); return false; + case Stream::STREAM_ABORTED: + // Handle this as connection reset. + NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_CONNECTION_RESET)); + return false; } NOTREACHED(); return false; @@ -167,7 +196,6 @@ void StreamURLRequestJob::NotifyFailure(int error_code) { // TODO(zork): Share these with BlobURLRequestJob. net::HttpStatusCode status_code = net::HTTP_INTERNAL_SERVER_ERROR; - std::string status_txt; switch (error_code) { case net::ERR_ACCESS_DENIED: status_code = net::HTTP_FORBIDDEN; diff --git a/chromium/content/browser/streams/stream_url_request_job_unittest.cc b/chromium/content/browser/streams/stream_url_request_job_unittest.cc index 4bb1798b514..8914fe5c50c 100644 --- a/chromium/content/browser/streams/stream_url_request_job_unittest.cc +++ b/chromium/content/browser/streams/stream_url_request_job_unittest.cc @@ -83,6 +83,7 @@ class StreamURLRequestJobTest : public testing::Test { // Verify response. EXPECT_TRUE(request_->status().is_success()); + ASSERT_TRUE(request_->response_headers()); EXPECT_EQ(expected_status_code, request_->response_headers()->response_code()); EXPECT_EQ(expected_response, delegate.data_received()); diff --git a/chromium/content/browser/tracing/OWNERS b/chromium/content/browser/tracing/OWNERS index 93d1471e1ef..9130707df06 100644 --- a/chromium/content/browser/tracing/OWNERS +++ b/chromium/content/browser/tracing/OWNERS @@ -1 +1,2 @@ nduca@chromium.org +dsinclair@chromium.org diff --git a/chromium/content/browser/tracing/trace_controller_impl.cc b/chromium/content/browser/tracing/trace_controller_impl.cc index 54e0c86cb42..fe08639876d 100644 --- a/chromium/content/browser/tracing/trace_controller_impl.cc +++ b/chromium/content/browser/tracing/trace_controller_impl.cc @@ -26,9 +26,11 @@ base::LazyInstance<TraceControllerImpl>::Leaky g_controller = class AutoStopTraceSubscriberStdio : public TraceSubscriberStdio { public: AutoStopTraceSubscriberStdio(const base::FilePath& file_path) - : TraceSubscriberStdio(file_path) {} + : TraceSubscriberStdio(file_path, + FILE_TYPE_PROPERTY_LIST, + false) {} - static void EndStartupTrace(TraceSubscriberStdio* subscriber) { + static void EndStartupTrace(AutoStopTraceSubscriberStdio* subscriber) { if (!TraceControllerImpl::GetInstance()->EndTracingAsync(subscriber)) delete subscriber; // else, the tracing will end asynchronously in OnEndTracingComplete(). @@ -146,6 +148,10 @@ bool TraceControllerImpl::EndTracingAsync(TraceSubscriber* subscriber) { if (!can_end_tracing() || subscriber != subscriber_) return false; + // Disable local trace early to avoid traces during end-tracing process from + // interfering with the process. + TraceLog::GetInstance()->SetDisabled(); + // There could be a case where there are no child processes and filters_ // is empty. In that case we can immediately tell the subscriber that tracing // has ended. To avoid recursive calls back to the subscriber, we will just @@ -299,20 +305,20 @@ void TraceControllerImpl::OnEndTracingAck( if (pending_end_ack_count_ == 0) return; - if (--pending_end_ack_count_ == 0) { - // All acks have been received. - is_tracing_ = false; - - // Disable local trace. - TraceLog::GetInstance()->SetDisabled(); - // During this call, our OnTraceDataCollected will be - // called with the last of the local trace data. Since we are on the UI - // thread, the call to OnTraceDataCollected will be synchronous, so we can - // immediately call OnEndTracingComplete below. + if (--pending_end_ack_count_ == 1) { + // All acks from subprocesses have been received. Now flush the local trace. + // During or after this call, our OnLocalTraceDataCollected will be + // called with the last of the local trace data. TraceLog::GetInstance()->Flush( - base::Bind(&TraceControllerImpl::OnTraceDataCollected, + base::Bind(&TraceControllerImpl::OnLocalTraceDataCollected, base::Unretained(this))); + } + + if (pending_end_ack_count_ == 0) { + // All acks (including from the subprocesses and the local trace) have been + // received. + is_tracing_ = false; // Trigger callback if one is set. if (subscriber_) { @@ -326,16 +332,6 @@ void TraceControllerImpl::OnEndTracingAck( is_get_category_groups_ = false; } - - if (pending_end_ack_count_ == 1) { - // The last ack represents local trace, so we need to ack it now. Note that - // this code only executes if there were child processes. - std::vector<std::string> category_groups; - TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&TraceControllerImpl::OnEndTracingAck, - base::Unretained(this), category_groups)); - } } void TraceControllerImpl::OnTraceDataCollected( @@ -354,6 +350,20 @@ void TraceControllerImpl::OnTraceDataCollected( subscriber_->OnTraceDataCollected(events_str_ptr); } +void TraceControllerImpl::OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events) { + if (events_str_ptr->data().size()) + OnTraceDataCollected(events_str_ptr); + + if (!has_more_events) { + // Simulate an EndTrackingAck for the local trace. + std::vector<std::string> category_groups; + TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); + OnEndTracingAck(category_groups); + } +} + void TraceControllerImpl::OnTraceNotification(int notification) { // OnTraceNotification may be called from any browser thread, either by the // local event trace system or from child processes via TraceMessageFilter. diff --git a/chromium/content/browser/tracing/trace_controller_impl.h b/chromium/content/browser/tracing/trace_controller_impl.h index f24d283a125..6f2ccb5d481 100644 --- a/chromium/content/browser/tracing/trace_controller_impl.h +++ b/chromium/content/browser/tracing/trace_controller_impl.h @@ -81,6 +81,11 @@ class TraceControllerImpl : public TraceController { void OnTraceNotification(int notification); void OnTraceBufferPercentFullReply(float percent_full); + // Callback of TraceLog::Flush() for the local trace. + void OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events); + FilterMap filters_; TraceSubscriber* subscriber_; // Pending acks for EndTracingAsync: diff --git a/chromium/content/browser/tracing/trace_message_filter.h b/chromium/content/browser/tracing/trace_message_filter.h index 8da34534604..41fd017827f 100644 --- a/chromium/content/browser/tracing/trace_message_filter.h +++ b/chromium/content/browser/tracing/trace_message_filter.h @@ -14,7 +14,7 @@ namespace content { // This class sends and receives trace messages on the browser process. -// See also: trace_controller.h +// See also: tracing_controller.h // See also: child_trace_message_filter.h class TraceMessageFilter : public BrowserMessageFilter { public: diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio.cc b/chromium/content/browser/tracing/trace_subscriber_stdio.cc index b5f6e828c60..2c6aede56ce 100644 --- a/chromium/content/browser/tracing/trace_subscriber_stdio.cc +++ b/chromium/content/browser/tracing/trace_subscriber_stdio.cc @@ -14,39 +14,79 @@ namespace content { // All method calls on this class are done on a SequencedWorkerPool thread. -class TraceSubscriberStdioImpl - : public base::RefCountedThreadSafe<TraceSubscriberStdioImpl> { +class TraceSubscriberStdio::TraceSubscriberStdioWorker + : public base::RefCountedThreadSafe<TraceSubscriberStdioWorker> { public: - explicit TraceSubscriberStdioImpl(const base::FilePath& path) + TraceSubscriberStdioWorker(const base::FilePath& path, + FileType file_type, + bool has_system_trace) : path_(path), - file_(0) {} - - void OnStart() { + file_type_(file_type), + has_system_trace_(has_system_trace), + file_(0), + needs_comma_(false), + wrote_trace_(false), + has_pending_system_trace_(false), + wrote_system_trace_(false) {} + + void OnTraceStart() { DCHECK(!file_); - trace_buffer_.SetOutputCallback( - base::Bind(&TraceSubscriberStdioImpl::Write, this)); file_ = file_util::OpenFile(path_, "w+"); - if (IsValid()) { - LOG(INFO) << "Logging performance trace to file: " << path_.value(); - trace_buffer_.Start(); - } else { + if (!IsValid()) { LOG(ERROR) << "Failed to open performance trace file: " << path_.value(); + return; } + + LOG(INFO) << "Logging performance trace to file: " << path_.value(); + if (file_type_ == FILE_TYPE_PROPERTY_LIST) + WriteString("{\"traceEvents\":"); + WriteString("["); } - void OnData(const scoped_refptr<base::RefCountedString>& data_ptr) { - trace_buffer_.AddFragment(data_ptr->data()); + void OnTraceData(const scoped_refptr<base::RefCountedString>& data_ptr) { + if (!IsValid()) + return; + DCHECK(!data_ptr->data().empty()); + if (needs_comma_) + WriteString(","); + WriteString(data_ptr->data()); + needs_comma_ = true; } - void OnEnd() { - trace_buffer_.Finish(); - CloseFile(); + void OnSystemTraceData( + const scoped_refptr<base::RefCountedString>& data_ptr) { + if (wrote_trace_) { + WriteSystemTrace(data_ptr); + End(); + } else { + pending_system_trace_ = data_ptr; + has_pending_system_trace_ = true; + } + } + + void OnTraceEnd() { + if (!IsValid()) + return; + WriteString("]"); + + wrote_trace_ = true; + + if (!has_system_trace_ || wrote_system_trace_) { + End(); + return; + } + + WriteString(","); + if (has_pending_system_trace_) { + WriteSystemTrace(pending_system_trace_); + End(); + } } private: - friend class base::RefCountedThreadSafe<TraceSubscriberStdioImpl>; + friend class base::RefCountedThreadSafe<TraceSubscriberStdioWorker>; - ~TraceSubscriberStdioImpl() { + ~TraceSubscriberStdioWorker() { CloseFile(); } @@ -58,32 +98,79 @@ class TraceSubscriberStdioImpl if (file_) { fclose(file_); file_ = 0; + + } + } + + void End() { + if (file_type_ == FILE_TYPE_PROPERTY_LIST) + WriteString("}"); + CloseFile(); + } + + void WriteSystemTrace(const scoped_refptr<base::RefCountedString>& data_ptr) { + // Newlines need to be replaced with the string "\n" to be parsed correctly. + // Double quotes need to be replaced with the string "\"". + // System logs are ASCII. + const std::string& data = data_ptr->data(); + const char* chars = data.c_str(); + WriteString("\"systemTraceEvents\":\""); + size_t old_index = 0; + for (size_t new_index = data.find_first_of("\n\""); + std::string::npos != new_index; + old_index = new_index + 1, + new_index = data.find_first_of("\n\"", old_index)) { + WriteChars(chars + old_index, new_index - old_index); + if (chars[new_index] == '\n') + WriteChars("\\n", 2); + else + WriteChars("\\\"", 2); } - // This is important, as it breaks a reference cycle. - trace_buffer_.SetOutputCallback( - base::debug::TraceResultBuffer::OutputCallback()); + WriteChars(chars + old_index, data.size() - old_index); + WriteString("\""); + wrote_system_trace_ = true; } - void Write(const std::string& output_str) { + void WriteChars(const char* output_chars, size_t size) { + if (size == 0) + return; + if (IsValid()) { - size_t written = fwrite(output_str.data(), 1, output_str.size(), file_); - if (written != output_str.size()) { + size_t written = fwrite(output_chars, 1, size, file_); + if (written != size) { LOG(ERROR) << "Error " << ferror(file_) << " in fwrite() to trace file"; CloseFile(); } } } + void WriteString(const std::string& output_str) { + WriteChars(output_str.data(), output_str.size()); + } + base::FilePath path_; + const FileType file_type_; + const bool has_system_trace_; FILE* file_; - base::debug::TraceResultBuffer trace_buffer_; + bool needs_comma_; + bool wrote_trace_; + bool has_pending_system_trace_; + bool wrote_system_trace_; + scoped_refptr<base::RefCountedString> pending_system_trace_; + DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdioWorker); }; -TraceSubscriberStdio::TraceSubscriberStdio(const base::FilePath& path) - : impl_(new TraceSubscriberStdioImpl(path)) { +TraceSubscriberStdio::TraceSubscriberStdio(const base::FilePath& path, + FileType file_type, + bool has_system_trace) + : worker_(new TraceSubscriberStdioWorker(path, + file_type, + has_system_trace)) { + if (has_system_trace) + CHECK_EQ(FILE_TYPE_PROPERTY_LIST, file_type); BrowserThread::PostBlockingPoolSequencedTask( __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioImpl::OnStart, impl_)); + base::Bind(&TraceSubscriberStdioWorker::OnTraceStart, worker_)); } TraceSubscriberStdio::~TraceSubscriberStdio() { @@ -92,14 +179,23 @@ TraceSubscriberStdio::~TraceSubscriberStdio() { void TraceSubscriberStdio::OnEndTracingComplete() { BrowserThread::PostBlockingPoolSequencedTask( __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioImpl::OnEnd, impl_)); + base::Bind(&TraceSubscriberStdioWorker::OnTraceEnd, worker_)); } void TraceSubscriberStdio::OnTraceDataCollected( const scoped_refptr<base::RefCountedString>& data_ptr) { BrowserThread::PostBlockingPoolSequencedTask( __FILE__, FROM_HERE, - base::Bind(&TraceSubscriberStdioImpl::OnData, impl_, data_ptr)); + base::Bind(&TraceSubscriberStdioWorker::OnTraceData, worker_, data_ptr)); +} + +void TraceSubscriberStdio::OnEndSystemTracing( + const scoped_refptr<base::RefCountedString>& events_str_ptr) { + BrowserThread::PostBlockingPoolSequencedTask( + __FILE__, FROM_HERE, + base::Bind(&TraceSubscriberStdioWorker::OnSystemTraceData, + worker_, + events_str_ptr)); } } // namespace content diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio.h b/chromium/content/browser/tracing/trace_subscriber_stdio.h index 06a70db8c88..b9fc4f74c19 100644 --- a/chromium/content/browser/tracing/trace_subscriber_stdio.h +++ b/chromium/content/browser/tracing/trace_subscriber_stdio.h @@ -17,14 +17,24 @@ class FilePath; namespace content { -class TraceSubscriberStdioImpl; - // Stdio implementation of TraceSubscriber. Use this to write traces to a file. class CONTENT_EXPORT TraceSubscriberStdio : NON_EXPORTED_BASE(public TraceSubscriber) { public: - // Creates or overwrites the specified file. Check IsValid() for success. - explicit TraceSubscriberStdio(const base::FilePath& path); + enum FileType { + // Output file as array, representing trace events: + // [event1, event2, ...] + FILE_TYPE_ARRAY, + // Output file as property list with one or two items: + // {traceEvents: [event1, event2, ...], + // systemTraceEvents: "event1\nevent2\n..." // optional} + FILE_TYPE_PROPERTY_LIST + }; + + // has_system_trace indicates whether system trace events are expected. + TraceSubscriberStdio(const base::FilePath& path, + FileType file_type, + bool has_system_trace); virtual ~TraceSubscriberStdio(); // Implementation of TraceSubscriber @@ -32,8 +42,13 @@ class CONTENT_EXPORT TraceSubscriberStdio virtual void OnTraceDataCollected( const scoped_refptr<base::RefCountedString>& data_ptr) OVERRIDE; + // To be used as callback to DebugDaemonClient::RequestStopSystemTracing(). + virtual void OnEndSystemTracing( + const scoped_refptr<base::RefCountedString>& events_str_ptr); + private: - scoped_refptr<TraceSubscriberStdioImpl> impl_; + class TraceSubscriberStdioWorker; + scoped_refptr<TraceSubscriberStdioWorker> worker_; DISALLOW_COPY_AND_ASSIGN(TraceSubscriberStdio); }; diff --git a/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc b/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc index 0b0e7c2ee80..10e51a968df 100644 --- a/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc +++ b/chromium/content/browser/tracing/trace_subscriber_stdio_unittest.cc @@ -14,12 +14,14 @@ namespace content { class TraceSubscriberStdioTest : public ::testing::Test {}; -TEST_F(TraceSubscriberStdioTest, CanWriteDataToFile) { +TEST_F(TraceSubscriberStdioTest, CanWriteArray) { base::ScopedTempDir trace_dir; ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); { - TraceSubscriberStdio subscriber(trace_file); + TraceSubscriberStdio subscriber(trace_file, + TraceSubscriberStdio::FILE_TYPE_ARRAY, + false); std::string foo("foo"); subscriber.OnTraceDataCollected( @@ -33,8 +35,98 @@ TEST_F(TraceSubscriberStdioTest, CanWriteDataToFile) { } BrowserThread::GetBlockingPool()->FlushForTesting(); std::string result; - EXPECT_TRUE(file_util::ReadFileToString(trace_file, &result)); + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); EXPECT_EQ("[foo,bar]", result); } +TEST_F(TraceSubscriberStdioTest, CanWritePropertyList) { + base::ScopedTempDir trace_dir; + ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); + base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); + { + TraceSubscriberStdio subscriber( + trace_file, + TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, + false); + + std::string foo("foo"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&foo))); + + std::string bar("bar"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&bar))); + + subscriber.OnEndTracingComplete(); + } + BrowserThread::GetBlockingPool()->FlushForTesting(); + std::string result; + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); + EXPECT_EQ("{\"traceEvents\":[foo,bar]}", result); +} + +TEST_F(TraceSubscriberStdioTest, CanWriteSystemDataFirst) { + base::ScopedTempDir trace_dir; + ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); + base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); + { + TraceSubscriberStdio subscriber( + trace_file, + TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, + true); + + std::string foo("foo"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&foo))); + + std::string bar("bar"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&bar))); + + std::string systemTrace("event1\nev\"ent\"2\n"); + subscriber.OnEndSystemTracing( + make_scoped_refptr(base::RefCountedString::TakeString(&systemTrace))); + subscriber.OnEndTracingComplete(); + } + BrowserThread::GetBlockingPool()->FlushForTesting(); + std::string result; + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); + EXPECT_EQ( + "{\"traceEvents\":[foo,bar],\"" + "systemTraceEvents\":\"event1\\nev\\\"ent\\\"2\\n\"}", + result); +} + +TEST_F(TraceSubscriberStdioTest, CanWriteSystemDataLast) { + base::ScopedTempDir trace_dir; + ASSERT_TRUE(trace_dir.CreateUniqueTempDir()); + base::FilePath trace_file(trace_dir.path().AppendASCII("trace.txt")); + { + TraceSubscriberStdio subscriber( + trace_file, + TraceSubscriberStdio::FILE_TYPE_PROPERTY_LIST, + true); + + std::string foo("foo"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&foo))); + + std::string bar("bar"); + subscriber.OnTraceDataCollected( + make_scoped_refptr(base::RefCountedString::TakeString(&bar))); + + std::string systemTrace("event1\nev\"ent\"2\n"); + subscriber.OnEndTracingComplete(); + subscriber.OnEndSystemTracing( + make_scoped_refptr(base::RefCountedString::TakeString(&systemTrace))); + } + BrowserThread::GetBlockingPool()->FlushForTesting(); + std::string result; + EXPECT_TRUE(base::ReadFileToString(trace_file, &result)); + EXPECT_EQ( + "{\"traceEvents\":[foo,bar],\"" + "systemTraceEvents\":\"event1\\nev\\\"ent\\\"2\\n\"}", + result); +} + } // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_browsertest.cc b/chromium/content/browser/tracing/tracing_controller_browsertest.cc new file mode 100644 index 00000000000..c88932e0597 --- /dev/null +++ b/chromium/content/browser/tracing/tracing_controller_browsertest.cc @@ -0,0 +1,115 @@ +// Copyright 2013 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 "base/file_util.h" +#include "base/run_loop.h" +#include "content/browser/tracing/tracing_controller_impl.h" +#include "content/public/test/browser_test_utils.h" +#include "content/shell/browser/shell.h" +#include "content/test/content_browser_test.h" +#include "content/test/content_browser_test_utils.h" + +namespace content { + +class TracingControllerTest : public ContentBrowserTest { + public: + TracingControllerTest() {} + + virtual void SetUp() OVERRIDE { + get_categories_done_callback_count_ = 0; + enable_recording_done_callback_count_ = 0; + disable_recording_done_callback_count_ = 0; + ContentBrowserTest::SetUp(); + } + + virtual void TearDown() OVERRIDE { + ContentBrowserTest::TearDown(); + } + + void Navigate(Shell* shell) { + NavigateToURL(shell, GetTestUrl("", "title.html")); + } + + void GetCategoriesDoneCallbackTest(base::Closure quit_callback, + const std::set<std::string>& categories) { + get_categories_done_callback_count_++; + EXPECT_TRUE(categories.size() > 0); + quit_callback.Run(); + } + + void EnableRecordingDoneCallbackTest(base::Closure quit_callback) { + enable_recording_done_callback_count_++; + quit_callback.Run(); + } + + void DisableRecordingDoneCallbackTest(base::Closure quit_callback, + scoped_ptr<base::FilePath> file_path) { + disable_recording_done_callback_count_++; + EXPECT_TRUE(PathExists(*file_path)); + quit_callback.Run(); + } + + int get_categories_done_callback_count() const { + return get_categories_done_callback_count_; + } + + int enable_recording_done_callback_count() const { + return enable_recording_done_callback_count_; + } + + int disable_recording_done_callback_count() const { + return disable_recording_done_callback_count_; + } + + private: + int get_categories_done_callback_count_; + int enable_recording_done_callback_count_; + int disable_recording_done_callback_count_; +}; + +IN_PROC_BROWSER_TEST_F(TracingControllerTest, GetCategories) { + Navigate(shell()); + + TracingController* controller = TracingController::GetInstance(); + + base::RunLoop run_loop; + TracingController::GetCategoriesDoneCallback callback = + base::Bind(&TracingControllerTest::GetCategoriesDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + controller->GetCategories(callback); + run_loop.Run(); + EXPECT_EQ(get_categories_done_callback_count(), 1); +} + +IN_PROC_BROWSER_TEST_F(TracingControllerTest, EnableAndDisableRecording) { + Navigate(shell()); + + TracingController* controller = TracingController::GetInstance(); + + { + base::RunLoop run_loop; + TracingController::EnableRecordingDoneCallback callback = + base::Bind(&TracingControllerTest::EnableRecordingDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + controller->EnableRecording(base::debug::CategoryFilter("*"), + TracingController::Options(), callback); + run_loop.Run(); + EXPECT_EQ(enable_recording_done_callback_count(), 1); + } + + { + base::RunLoop run_loop; + TracingController::TracingFileResultCallback callback = + base::Bind(&TracingControllerTest::DisableRecordingDoneCallbackTest, + base::Unretained(this), + run_loop.QuitClosure()); + controller->DisableRecording(callback); + run_loop.Run(); + EXPECT_EQ(disable_recording_done_callback_count(), 1); + } +} + +} // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_impl.cc b/chromium/content/browser/tracing/tracing_controller_impl.cc new file mode 100644 index 00000000000..1c1c43aabf9 --- /dev/null +++ b/chromium/content/browser/tracing/tracing_controller_impl.cc @@ -0,0 +1,270 @@ +// Copyright 2013 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/browser/tracing/tracing_controller_impl.h" + +#include "base/bind.h" +#include "base/file_util.h" +#include "base/json/string_escape.h" +#include "base/strings/string_number_conversions.h" +#include "content/browser/tracing/trace_message_filter.h" +#include "content/common/child_process_messages.h" +#include "content/public/browser/browser_message_filter.h" +#include "content/public/common/content_switches.h" + +using base::debug::TraceLog; + +namespace content { + +namespace { + +base::LazyInstance<TracingControllerImpl>::Leaky g_controller = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +TracingController* TracingController::GetInstance() { + return TracingControllerImpl::GetInstance(); +} + +TracingControllerImpl::TracingControllerImpl() : + pending_end_ack_count_(0), + is_recording_(false), + category_filter_( + base::debug::CategoryFilter::kDefaultCategoryFilterString) { +} + +TracingControllerImpl::~TracingControllerImpl() { + // This is a Leaky instance. + NOTREACHED(); +} + +TracingControllerImpl* TracingControllerImpl::GetInstance() { + return g_controller.Pointer(); +} + +void TracingControllerImpl::GetCategories( + const GetCategoriesDoneCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Known categories come back from child processes with the EndTracingAck + // message. So to get known categories, just begin and end tracing immediately + // afterwards. This will ping all the child processes for categories. + pending_get_categories_done_callback_ = callback; + EnableRecording(base::debug::CategoryFilter("*"), + TracingController::Options(), + EnableRecordingDoneCallback()); + DisableRecording(TracingFileResultCallback()); +} + +void TracingControllerImpl::EnableRecording( + const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableRecordingDoneCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_enable_recording()) + return; + + trace_options_ = TraceLog::GetInstance()->trace_options(); + TraceLog::GetInstance()->SetEnabled(filter, trace_options_); + + is_recording_ = true; + category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter(); + + // Notify all child processes. + for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { + it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_); + } + + if (!callback.is_null()) + callback.Run(); +} + +void TracingControllerImpl::DisableRecording( + const TracingFileResultCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (!can_end_recording()) + return; + + pending_disable_recording_done_callback_ = callback; + + // Disable local trace early to avoid traces during end-tracing process from + // interfering with the process. + TraceLog::GetInstance()->SetDisabled(); + + // We don't need to create a temporary file when getting categories. + if (pending_get_categories_done_callback_.is_null()) { + base::FilePath temporary_file; + file_util::CreateTemporaryFile(&temporary_file); + recording_result_file_.reset(new base::FilePath(temporary_file)); + } + + // There could be a case where there are no child processes and filters_ + // is empty. In that case we can immediately tell the subscriber that tracing + // has ended. To avoid recursive calls back to the subscriber, we will just + // use the existing asynchronous OnDisableRecordingAcked code. + // Count myself (local trace) in pending_end_ack_count_, acked below. + pending_end_ack_count_ = filters_.size() + 1; + + // Handle special case of zero child processes. + if (pending_end_ack_count_ == 1) { + // Ack asynchronously now, because we don't have any children to wait for. + std::vector<std::string> category_groups; + TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnDisableRecordingAcked, + base::Unretained(this), category_groups)); + } + + // Notify all child processes. + for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) { + it->get()->SendEndTracing(); + } +} + +void TracingControllerImpl::EnableMonitoring( + const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableMonitoringDoneCallback& callback) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::DisableMonitoring( + const DisableMonitoringDoneCallback& callback) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::GetMonitoringStatus( + bool* out_enabled, + base::debug::CategoryFilter* out_filter, + TracingController::Options* out_options) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::CaptureCurrentMonitoringSnapshot( + const TracingFileResultCallback& callback) { + NOTIMPLEMENTED(); +} + +void TracingControllerImpl::AddFilter(TraceMessageFilter* filter) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::AddFilter, base::Unretained(this), + make_scoped_refptr(filter))); + return; + } + + filters_.insert(filter); + if (is_recording_enabled()) { + std::string cf_str = category_filter_.ToString(); + filter->SendBeginTracing(cf_str, trace_options_); + } +} + +void TracingControllerImpl::RemoveFilter(TraceMessageFilter* filter) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::RemoveFilter, base::Unretained(this), + make_scoped_refptr(filter))); + return; + } + + filters_.erase(filter); +} + +void TracingControllerImpl::OnDisableRecordingAcked( + const std::vector<std::string>& known_category_groups) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnDisableRecordingAcked, + base::Unretained(this), known_category_groups)); + return; + } + + // Merge known_category_groups with known_category_groups_ + known_category_groups_.insert(known_category_groups.begin(), + known_category_groups.end()); + + if (pending_end_ack_count_ == 0) + return; + + if (--pending_end_ack_count_ == 1) { + // All acks from subprocesses have been received. Now flush the local trace. + // During or after this call, our OnLocalTraceDataCollected will be + // called with the last of the local trace data. + TraceLog::GetInstance()->Flush( + base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected, + base::Unretained(this))); + } + + if (pending_end_ack_count_ != 0) + return; + + // All acks (including from the subprocesses and the local trace) have been + // received. + is_recording_ = false; + + // Trigger callback if one is set. + if (!pending_get_categories_done_callback_.is_null()) { + pending_get_categories_done_callback_.Run(known_category_groups_); + pending_get_categories_done_callback_.Reset(); + } else { + OnEndTracingComplete(); + } +} + +void TracingControllerImpl::OnEndTracingComplete() { + if (pending_disable_recording_done_callback_.is_null()) + return; + + pending_disable_recording_done_callback_.Run(recording_result_file_.Pass()); + pending_disable_recording_done_callback_.Reset(); +} + +void TracingControllerImpl::OnTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr) { + // OnTraceDataCollected may be called from any browser thread, either by the + // local event trace system or from child processes via TraceMessageFilter. + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TracingControllerImpl::OnTraceDataCollected, + base::Unretained(this), events_str_ptr)); + return; + } + + // Drop trace events if we are just getting categories. + if (!pending_get_categories_done_callback_.is_null()) + return; + + std::string javascript; + javascript.reserve(events_str_ptr->size() * 2); + base::JsonDoubleQuote(events_str_ptr->data(), false, &javascript); + + // Intentionally append a , to the traceData. This technically causes all + // traceData that we pass back to JS to end with a comma, but that is + // actually something the JS side strips away anyway + javascript.append(","); + + file_util::WriteFile(*recording_result_file_, + javascript.c_str(), javascript.length()); +} + +void TracingControllerImpl::OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events) { + if (events_str_ptr->data().size()) + OnTraceDataCollected(events_str_ptr); + + if (has_more_events) + return; + + // Simulate an DisableRecordingAcked for the local trace. + std::vector<std::string> category_groups; + TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups); + OnDisableRecordingAcked(category_groups); +} + +} // namespace content diff --git a/chromium/content/browser/tracing/tracing_controller_impl.h b/chromium/content/browser/tracing/tracing_controller_impl.h new file mode 100644 index 00000000000..22192255444 --- /dev/null +++ b/chromium/content/browser/tracing/tracing_controller_impl.h @@ -0,0 +1,100 @@ +// Copyright 2013 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_BROWSER_TRACING_TRACING_CONTROLLER_IMPL_H_ +#define CONTENT_BROWSER_TRACING_TRACING_CONTROLLER_IMPL_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/lazy_instance.h" +#include "content/public/browser/trace_subscriber.h" +#include "content/public/browser/tracing_controller.h" + +namespace content { + +class TraceMessageFilter; + +class TracingControllerImpl : + public TracingController, public TraceSubscriber { + public: + static TracingControllerImpl* GetInstance(); + + // TracingController implementation. + virtual void GetCategories( + const GetCategoriesDoneCallback& callback) OVERRIDE; + virtual void EnableRecording( + const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableRecordingDoneCallback& callback) OVERRIDE; + virtual void DisableRecording( + const TracingFileResultCallback& callback) OVERRIDE; + virtual void EnableMonitoring(const base::debug::CategoryFilter& filter, + TracingController::Options options, + const EnableMonitoringDoneCallback& callback) OVERRIDE; + virtual void DisableMonitoring( + const DisableMonitoringDoneCallback& callback) OVERRIDE; + virtual void GetMonitoringStatus( + bool* out_enabled, + base::debug::CategoryFilter* out_filter, + TracingController::Options* out_options) OVERRIDE; + virtual void CaptureCurrentMonitoringSnapshot( + const TracingFileResultCallback& callback) OVERRIDE; + + private: + typedef std::set<scoped_refptr<TraceMessageFilter> > FilterMap; + + friend struct base::DefaultLazyInstanceTraits<TracingControllerImpl>; + friend class TraceMessageFilter; + + TracingControllerImpl(); + virtual ~TracingControllerImpl(); + + // TraceSubscriber implementation. + virtual void OnEndTracingComplete() OVERRIDE; + virtual void OnTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr) OVERRIDE; + + bool can_enable_recording() const { + return !is_recording_; + } + + bool can_end_recording() const { + return is_recording_ && pending_end_ack_count_ == 0; + } + + bool is_recording_enabled() const { + return can_end_recording(); + } + + // Methods for use by TraceMessageFilter. + void AddFilter(TraceMessageFilter* filter); + void RemoveFilter(TraceMessageFilter* filter); + + // Callback of TraceLog::Flush() for the local trace. + void OnLocalTraceDataCollected( + const scoped_refptr<base::RefCountedString>& events_str_ptr, + bool has_more_events); + + void OnDisableRecordingAcked( + const std::vector<std::string>& known_category_groups); + + FilterMap filters_; + // Pending acks for DisableRecording. + int pending_end_ack_count_; + bool is_recording_; + GetCategoriesDoneCallback pending_get_categories_done_callback_; + TracingFileResultCallback pending_disable_recording_done_callback_; + std::set<std::string> known_category_groups_; + base::debug::TraceLog::Options trace_options_; + base::debug::CategoryFilter category_filter_; + scoped_ptr<base::FilePath> recording_result_file_; + + DISALLOW_COPY_AND_ASSIGN(TracingControllerImpl); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_TRACING_TRACING_CONTROLLER_IMPL_H_ diff --git a/chromium/content/browser/tracing/tracing_ui.cc b/chromium/content/browser/tracing/tracing_ui.cc index aab652d5bf1..15bd2cf2590 100644 --- a/chromium/content/browser/tracing/tracing_ui.cc +++ b/chromium/content/browser/tracing/tracing_ui.cc @@ -13,6 +13,7 @@ #include "base/file_util.h" #include "base/json/string_escape.h" #include "base/memory/scoped_ptr.h" +#include "base/safe_numerics.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -225,7 +226,7 @@ void TracingMessageHandler::OnBeginRequestBufferPercentFull( // TaskProxy callback when reading is complete. void ReadTraceFileCallback(TaskProxy* proxy, const base::FilePath& path) { std::string file_contents; - if (!file_util::ReadFileToString(path, &file_contents)) + if (!base::ReadFileToString(path, &file_contents)) return; // We need to escape the file contents, because it will go into a javascript @@ -269,7 +270,8 @@ void ReadTraceFileCallback(TaskProxy* proxy, const base::FilePath& path) { void WriteTraceFileCallback(TaskProxy* proxy, const base::FilePath& path, std::string* contents) { - if (!file_util::WriteFile(path, contents->c_str(), contents->size())) + int size = base::checked_numeric_cast<int>(contents->size()); + if (file_util::WriteFile(path, contents->c_str(), size) != size) return; BrowserThread::PostTask( @@ -363,7 +365,7 @@ void TracingMessageHandler::OnSaveTraceFile(const base::ListValue* list) { if (select_trace_file_dialog_.get()) return; - DCHECK(list->GetSize() == 1); + DCHECK_EQ(1U, list->GetSize()); std::string* trace_data = new std::string(); bool ok = list->GetString(0, trace_data); diff --git a/chromium/content/browser/utility_process_host_impl.cc b/chromium/content/browser/utility_process_host_impl.cc index 2e562188d24..f2b3bc31dee 100644 --- a/chromium/content/browser/utility_process_host_impl.cc +++ b/chromium/content/browser/utility_process_host_impl.cc @@ -16,7 +16,6 @@ #include "base/synchronization/waitable_event.h" #include "content/browser/browser_child_process_host_impl.h" #include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/child/child_process.h" #include "content/common/child_process_host_impl.h" #include "content/common/utility_messages.h" #include "content/public/browser/browser_thread.h" @@ -24,7 +23,6 @@ #include "content/public/browser/utility_process_host_client.h" #include "content/public/common/content_switches.h" #include "content/public/common/process_type.h" -#include "content/utility/utility_thread_impl.h" #include "ipc/ipc_switches.h" #include "ui/base/ui_base_switches.h" @@ -53,53 +51,8 @@ private: }; #endif -// We want to ensure there's only one utility thread running at a time, as there -// are many globals used in the utility process. -static base::LazyInstance<base::Lock> g_one_utility_thread_lock; -// Single process not supported in multiple dll mode currently. -#if !defined(CHROME_MULTIPLE_DLL) -class UtilityMainThread : public base::Thread { - public: - UtilityMainThread(const std::string& channel_id) - : Thread("Chrome_InProcUtilityThread"), - channel_id_(channel_id) { - } - - virtual ~UtilityMainThread() { - Stop(); - } - - private: - // base::Thread implementation: - virtual void Init() OVERRIDE { - // We need to return right away or else the main thread that started us will - // hang. - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&UtilityMainThread::InitInternal, base::Unretained(this))); - } - - virtual void CleanUp() OVERRIDE { - child_process_.reset(); - - // See comment in RendererMainThread. - SetThreadWasQuitProperly(true); - g_one_utility_thread_lock.Get().Release(); - } - - void InitInternal() { - g_one_utility_thread_lock.Get().Acquire(); - child_process_.reset(new ChildProcess()); - child_process_->set_main_thread(new UtilityThreadImpl(channel_id_)); - } - - std::string channel_id_; - scoped_ptr<ChildProcess> child_process_; - - DISALLOW_COPY_AND_ASSIGN(UtilityMainThread); -}; -#endif // !CHROME_MULTIPLE_DLL +UtilityMainThreadFactoryFunction g_utility_main_thread_factory = NULL; UtilityProcessHost* UtilityProcessHost::Create( UtilityProcessHostClient* client, @@ -107,6 +60,11 @@ UtilityProcessHost* UtilityProcessHost::Create( return new UtilityProcessHostImpl(client, client_task_runner); } +void UtilityProcessHost::RegisterUtilityMainThreadFactory( + UtilityMainThreadFactoryFunction create) { + g_utility_main_thread_factory = create; +} + UtilityProcessHostImpl::UtilityProcessHostImpl( UtilityProcessHostClient* client, base::SequencedTaskRunner* client_task_runner) @@ -172,7 +130,7 @@ const ChildProcessData& UtilityProcessHostImpl::GetData() { #if defined(OS_POSIX) -void UtilityProcessHostImpl::SetEnv(const base::EnvironmentVector& env) { +void UtilityProcessHostImpl::SetEnv(const base::EnvironmentMap& env) { env_ = env; } @@ -196,15 +154,13 @@ bool UtilityProcessHostImpl::StartProcess() { return false; // Single process not supported in multiple dll mode currently. -#if !defined(CHROME_MULTIPLE_DLL) - if (RenderProcessHost::run_renderer_in_process()) { + if (RenderProcessHost::run_renderer_in_process() && + g_utility_main_thread_factory) { // See comment in RenderProcessHostImpl::Init() for the background on why we // support single process mode this way. - in_process_thread_.reset(new UtilityMainThread(channel_id)); + in_process_thread_.reset(g_utility_main_thread_factory(channel_id)); in_process_thread_->Start(); - } else -#endif // !CHROME_MULTIPLE_DLL - { + } else { const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); int child_flags = child_flags_; @@ -247,13 +203,14 @@ bool UtilityProcessHostImpl::StartProcess() { // TODO(port): Sandbox this on Linux. Also, zygote this to work with // Linux updating. if (has_cmd_prefix) { - // launch the utility child process with some prefix (usually "xterm -e gdb - // --args"). + // launch the utility child process with some prefix + // (usually "xterm -e gdb --args"). cmd_line->PrependWrapper(browser_command_line.GetSwitchValueNative( switches::kUtilityCmdPrefix)); } - cmd_line->AppendSwitchPath(switches::kUtilityProcessAllowedDir, exposed_dir_); + cmd_line->AppendSwitchPath(switches::kUtilityProcessAllowedDir, + exposed_dir_); #endif if (is_mdns_enabled_) diff --git a/chromium/content/browser/utility_process_host_impl.h b/chromium/content/browser/utility_process_host_impl.h index 6eecd3d8985..039e20f8c50 100644 --- a/chromium/content/browser/utility_process_host_impl.h +++ b/chromium/content/browser/utility_process_host_impl.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -18,11 +19,11 @@ namespace base { class SequencedTaskRunner; +class Thread; } namespace content { class BrowserChildProcessHostImpl; -class UtilityMainThread; class CONTENT_EXPORT UtilityProcessHostImpl : public NON_EXPORTED_BASE(UtilityProcessHost), @@ -42,7 +43,7 @@ class CONTENT_EXPORT UtilityProcessHostImpl virtual void EnableZygote() OVERRIDE; virtual const ChildProcessData& GetData() OVERRIDE; #if defined(OS_POSIX) - virtual void SetEnv(const base::EnvironmentVector& env) OVERRIDE; + virtual void SetEnv(const base::EnvironmentMap& env) OVERRIDE; #endif void set_child_flags(int flags) { child_flags_ = flags; } @@ -77,16 +78,14 @@ class CONTENT_EXPORT UtilityProcessHostImpl // Launch the utility process from the zygote. Defaults to false. bool use_linux_zygote_; - base::EnvironmentVector env_; + base::EnvironmentMap env_; bool started_; scoped_ptr<BrowserChildProcessHostImpl> process_; -#if !defined(CHROME_MULTIPLE_DLL) // Used in single-process mode instead of process_. - scoped_ptr<UtilityMainThread> in_process_thread_; -#endif + scoped_ptr<base::Thread> in_process_thread_; DISALLOW_COPY_AND_ASSIGN(UtilityProcessHostImpl); }; diff --git a/chromium/content/browser/web_contents/aura/image_window_delegate.cc b/chromium/content/browser/web_contents/aura/image_window_delegate.cc index 1829f46ba44..dbb066c894d 100644 --- a/chromium/content/browser/web_contents/aura/image_window_delegate.cc +++ b/chromium/content/browser/web_contents/aura/image_window_delegate.cc @@ -4,6 +4,7 @@ #include "content/browser/web_contents/aura/image_window_delegate.h" +#include "ui/base/cursor/cursor.h" #include "ui/base/hit_test.h" #include "ui/compositor/compositor.h" #include "ui/gfx/canvas.h" @@ -93,8 +94,8 @@ bool ImageWindowDelegate::HasHitTestMask() const { void ImageWindowDelegate::GetHitTestMask(gfx::Path* mask) const { } -scoped_refptr<ui::Texture> ImageWindowDelegate::CopyTexture() { - return scoped_refptr<ui::Texture>(); +void ImageWindowDelegate::DidRecreateLayer(ui::Layer *old_layer, + ui::Layer *new_layer) { } } // namespace content diff --git a/chromium/content/browser/web_contents/aura/image_window_delegate.h b/chromium/content/browser/web_contents/aura/image_window_delegate.h index ad92ce28f91..561fac8a109 100644 --- a/chromium/content/browser/web_contents/aura/image_window_delegate.h +++ b/chromium/content/browser/web_contents/aura/image_window_delegate.h @@ -42,7 +42,8 @@ class ImageWindowDelegate : public aura::WindowDelegate { virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; virtual bool HasHitTestMask() const OVERRIDE; virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; - virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE; + virtual void DidRecreateLayer(ui::Layer* old_layer, + ui::Layer* new_layer) OVERRIDE; protected: gfx::Image image_; diff --git a/chromium/content/browser/web_contents/aura/window_slider.cc b/chromium/content/browser/web_contents/aura/window_slider.cc index 7afd32dd3d7..c9c3797b54c 100644 --- a/chromium/content/browser/web_contents/aura/window_slider.cc +++ b/chromium/content/browser/web_contents/aura/window_slider.cc @@ -11,9 +11,9 @@ #include "content/browser/web_contents/aura/shadow_layer_delegate.h" #include "content/public/browser/overscroll_configuration.h" #include "ui/aura/window.h" -#include "ui/base/events/event.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/events/event.h" namespace content { @@ -58,8 +58,11 @@ WindowSlider::WindowSlider(Delegate* delegate, owner_(owner), delta_x_(0.f), weak_factory_(this), - horiz_start_threshold_(content::GetOverscrollConfig( - content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START)), + active_start_threshold_(0.f), + start_threshold_touchscreen_(content::GetOverscrollConfig( + content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN)), + start_threshold_touchpad_(content::GetOverscrollConfig( + content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD)), complete_threshold_(content::GetOverscrollConfig( content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)) { event_window_->AddPreTargetHandler(this); @@ -89,7 +92,7 @@ void WindowSlider::ChangeOwner(aura::Window* new_owner) { } bool WindowSlider::IsSlideInProgress() const { - return fabs(delta_x_) >= horiz_start_threshold_ || slider_.get() || + return fabs(delta_x_) >= active_start_threshold_ || slider_.get() || weak_factory_.HasWeakPtrs(); } @@ -107,7 +110,7 @@ void WindowSlider::SetupSliderLayer() { void WindowSlider::UpdateForScroll(float x_offset, float y_offset) { float old_delta = delta_x_; delta_x_ += x_offset; - if (fabs(delta_x_) < horiz_start_threshold_ && !slider_.get()) + if (fabs(delta_x_) < active_start_threshold_ && !slider_.get()) return; if ((old_delta < 0 && delta_x_ > 0) || @@ -127,13 +130,13 @@ void WindowSlider::UpdateForScroll(float x_offset, float y_offset) { SetupSliderLayer(); } - if (delta_x_ <= -horiz_start_threshold_) { + if (delta_x_ <= -active_start_threshold_) { translate = owner_->bounds().width() + - std::max(delta_x_ + horiz_start_threshold_, + std::max(delta_x_ + active_start_threshold_, static_cast<float>(-owner_->bounds().width())); translate_layer = slider_.get(); - } else if (delta_x_ >= horiz_start_threshold_) { - translate = std::min(delta_x_ - horiz_start_threshold_, + } else if (delta_x_ >= active_start_threshold_) { + translate = std::min(delta_x_ - active_start_threshold_, static_cast<float>(owner_->bounds().width())); translate_layer = owner_->layer(); } else { @@ -153,7 +156,7 @@ void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) { return; int width = owner_->bounds().width(); - float ratio = (fabs(delta_x_) - horiz_start_threshold_) / width; + float ratio = (fabs(delta_x_) - active_start_threshold_) / width; if (ratio < complete_threshold_) { ResetScroll(); return; @@ -163,7 +166,7 @@ void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) { ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator()); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - settings.SetTweenType(ui::Tween::EASE_OUT); + settings.SetTweenType(gfx::Tween::EASE_OUT); settings.AddObserver(new CallbackAnimationObserver( base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation, weak_factory_.GetWeakPtr()))); @@ -187,7 +190,7 @@ void WindowSlider::ResetScroll() { ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - settings.SetTweenType(ui::Tween::EASE_OUT); + settings.SetTweenType(gfx::Tween::EASE_OUT); // Delete the layer and the shadow at the end of the animation. settings.AddObserver(new CallbackAnimationObserver( @@ -205,7 +208,7 @@ void WindowSlider::ResetScroll() { ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator()); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - settings.SetTweenType(ui::Tween::EASE_OUT); + settings.SetTweenType(gfx::Tween::EASE_OUT); settings.AddObserver(new CallbackAnimationObserver( base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation, weak_factory_.GetWeakPtr()))); @@ -245,6 +248,7 @@ void WindowSlider::OnMouseEvent(ui::MouseEvent* event) { } void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) { + active_start_threshold_ = start_threshold_touchpad_; if (event->type() == ui::ET_SCROLL) UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal()); else if (event->type() == ui::ET_SCROLL_FLING_START) @@ -255,6 +259,7 @@ void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) { } void WindowSlider::OnGestureEvent(ui::GestureEvent* event) { + active_start_threshold_ = start_threshold_touchscreen_; const ui::GestureEventDetails& details = event->details(); switch (event->type()) { case ui::ET_GESTURE_SCROLL_BEGIN: diff --git a/chromium/content/browser/web_contents/aura/window_slider.h b/chromium/content/browser/web_contents/aura/window_slider.h index 227ad1ba82c..7100bf5c475 100644 --- a/chromium/content/browser/web_contents/aura/window_slider.h +++ b/chromium/content/browser/web_contents/aura/window_slider.h @@ -10,7 +10,7 @@ #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "ui/aura/window_observer.h" -#include "ui/base/events/event_handler.h" +#include "ui/events/event_handler.h" namespace ui { class Layer; @@ -120,7 +120,10 @@ class CONTENT_EXPORT WindowSlider : public ui::EventHandler, base::WeakPtrFactory<WindowSlider> weak_factory_; - const float horiz_start_threshold_; + float active_start_threshold_; + + const float start_threshold_touchscreen_; + const float start_threshold_touchpad_; const float complete_threshold_; DISALLOW_COPY_AND_ASSIGN(WindowSlider); diff --git a/chromium/content/browser/web_contents/interstitial_page_impl.cc b/chromium/content/browser/web_contents/interstitial_page_impl.cc index 232217d368f..bb93dc3d6c9 100644 --- a/chromium/content/browser/web_contents/interstitial_page_impl.cc +++ b/chromium/content/browser/web_contents/interstitial_page_impl.cc @@ -244,7 +244,7 @@ void InterstitialPageImpl::Hide() { RenderWidgetHostView* old_view = web_contents_->GetRenderViewHost()->GetView(); if (web_contents_->GetInterstitialPage() == this && - old_view && !old_view->IsShowing()) { + old_view && !old_view->IsShowing() && !web_contents_->IsHidden()) { // Show the original RVH since we're going away. Note it might not exist if // the renderer crashed while the interstitial was showing. // Note that it is important that we don't call Show() if the view is @@ -378,7 +378,8 @@ void InterstitialPageImpl::DidNavigate( } // The RenderViewHost has loaded its contents, we can show it now. - render_view_host_->GetView()->Show(); + if (!web_contents_->IsHidden()) + render_view_host_->GetView()->Show(); web_contents_->AttachInterstitialPage(this); RenderWidgetHostView* rwh_view = @@ -503,6 +504,7 @@ RenderViewHost* InterstitialPageImpl::CreateRenderViewHost() { this, MSG_ROUTING_NONE, MSG_ROUTING_NONE, + false, false); web_contents_->RenderViewForInterstitialPageCreated(render_view_host); return render_view_host; diff --git a/chromium/content/browser/web_contents/navigation_controller_impl.cc b/chromium/content/browser/web_contents/navigation_controller_impl.cc index 2c0596d9c83..faad72b5f38 100644 --- a/chromium/content/browser/web_contents/navigation_controller_impl.cc +++ b/chromium/content/browser/web_contents/navigation_controller_impl.cc @@ -637,8 +637,8 @@ void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) { case LOAD_TYPE_DEFAULT: break; case LOAD_TYPE_BROWSER_INITIATED_HTTP_POST: - if (!params.url.SchemeIs(chrome::kHttpScheme) && - !params.url.SchemeIs(chrome::kHttpsScheme)) { + if (!params.url.SchemeIs(kHttpScheme) && + !params.url.SchemeIs(kHttpsScheme)) { NOTREACHED() << "Http post load must use http(s) scheme."; return; } @@ -798,6 +798,7 @@ bool NavigationControllerImpl::RendererDidNavigate( NavigationEntryImpl* active_entry = NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry()); active_entry->SetTimestamp(timestamp); + active_entry->SetHttpStatusCode(params.http_status_code); active_entry->SetPageState(params.page_state); // No longer needed since content state will hold the post data if any. active_entry->SetBrowserInitiatedPostData(NULL); @@ -1415,6 +1416,10 @@ bool NavigationControllerImpl::NeedsReload() const { return needs_reload_; } +void NavigationControllerImpl::SetNeedsReload() { + needs_reload_ = true; +} + void NavigationControllerImpl::RemoveEntryAtIndexInternal(int index) { DCHECK(index < GetEntryCount()); DCHECK(index != last_committed_entry_index_); @@ -1557,21 +1562,18 @@ void NavigationControllerImpl::NavigateToPendingEntry(ReloadType reload_type) { void NavigationControllerImpl::NotifyNavigationEntryCommitted( LoadCommittedDetails* details) { details->entry = GetActiveEntry(); - NotificationDetails notification_details = - Details<LoadCommittedDetails>(details); // We need to notify the ssl_manager_ before the web_contents_ so the // location bar will have up-to-date information about the security style // when it wants to draw. See http://crbug.com/11157 - ssl_manager_.DidCommitProvisionalLoad(notification_details); + ssl_manager_.DidCommitProvisionalLoad(*details); - // TODO(pkasting): http://b/1113079 Probably these explicit notification paths - // should be removed, and interested parties should just listen for the - // notification below instead. web_contents_->NotifyNavigationStateChanged(kInvalidateAll); - web_contents_->NotifyNavigationEntryCommitted(*details); + // TODO(avi): Remove. http://crbug.com/170921 + NotificationDetails notification_details = + Details<LoadCommittedDetails>(details); NotificationService::current()->Notify( NOTIFICATION_NAV_ENTRY_COMMITTED, Source<NavigationController>(this), diff --git a/chromium/content/browser/web_contents/navigation_controller_impl.h b/chromium/content/browser/web_contents/navigation_controller_impl.h index 2f0d71ea966..6e6f333506c 100644 --- a/chromium/content/browser/web_contents/navigation_controller_impl.h +++ b/chromium/content/browser/web_contents/navigation_controller_impl.h @@ -77,6 +77,7 @@ class CONTENT_EXPORT NavigationControllerImpl virtual void SetMaxRestoredPageID(int32 max_id) OVERRIDE; virtual int32 GetMaxRestoredPageID() const OVERRIDE; virtual bool NeedsReload() const OVERRIDE; + virtual void SetNeedsReload() OVERRIDE; virtual void CancelPendingReload() OVERRIDE; virtual void ContinuePendingReload() OVERRIDE; virtual bool IsInitialNavigation() const OVERRIDE; @@ -366,7 +367,7 @@ class CONTENT_EXPORT NavigationControllerImpl // of the restored entries to update its max page ID. int32 max_restored_page_id_; - // Manages the SSL security UI + // Manages the SSL security UI. SSLManager ssl_manager_; // Whether we need to be reloaded when made active. diff --git a/chromium/content/browser/web_contents/navigation_controller_impl_unittest.cc b/chromium/content/browser/web_contents/navigation_controller_impl_unittest.cc index df6a47c1b13..e91ad13fe72 100644 --- a/chromium/content/browser/web_contents/navigation_controller_impl_unittest.cc +++ b/chromium/content/browser/web_contents/navigation_controller_impl_unittest.cc @@ -355,8 +355,9 @@ TEST_F(NavigationControllerTest, LoadURL) { EXPECT_FALSE(controller.CanGoForward()); EXPECT_EQ(contents()->GetMaxPageID(), -1); - // The timestamp should not have been set yet. + // Neither the timestamp nor the status code should have been set yet. EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null()); + EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode()); // We should have gotten no notifications from the preceeding checks. EXPECT_EQ(0U, notifications.size()); @@ -2655,6 +2656,20 @@ TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) { contents()->DidStartProvisionalLoadForFrame( test_rvh(), 1, -1, true, GURL(kUnreachableWebDataURL)); EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL()); + + // We should remember if the pending entry will replace the current one. + // http://crbug.com/308444. + contents()->DidStartProvisionalLoadForFrame( + test_rvh(), 1, -1, true, url1); + NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> + set_should_replace_entry(true); + contents()->DidStartProvisionalLoadForFrame( + test_rvh(), 1, -1, true, url2); + EXPECT_TRUE( + NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())-> + should_replace_entry()); + test_rvh()->SendNavigate(0, url2); + EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL()); } // Tests that the URLs for renderer-initiated navigations are not displayed to @@ -2912,6 +2927,23 @@ TEST_F(NavigationControllerTest, CloneOmitsInterstitials) { ASSERT_EQ(2, clone->GetController().GetEntryCount()); } +// Test requesting and triggering a lazy reload. +TEST_F(NavigationControllerTest, LazyReload) { + NavigationControllerImpl& controller = controller_impl(); + const GURL url("http://foo"); + NavigateAndCommit(url); + ASSERT_FALSE(controller.NeedsReload()); + + // Request a reload to happen when the controller becomes active (e.g. after + // the renderer gets killed in background on Android). + controller.SetNeedsReload(); + ASSERT_TRUE(controller.NeedsReload()); + + // Set the controller as active, triggering the requested reload. + controller.SetActive(true); + ASSERT_FALSE(controller.NeedsReload()); +} + // Tests a subframe navigation while a toplevel navigation is pending. // http://crbug.com/43967 TEST_F(NavigationControllerTest, SubframeWhilePending) { diff --git a/chromium/content/browser/web_contents/navigation_entry_impl.cc b/chromium/content/browser/web_contents/navigation_entry_impl.cc index a23b7b6623a..10d50ca0df2 100644 --- a/chromium/content/browser/web_contents/navigation_entry_impl.cc +++ b/chromium/content/browser/web_contents/navigation_entry_impl.cc @@ -10,7 +10,7 @@ #include "content/public/common/content_constants.h" #include "content/public/common/url_constants.h" #include "net/base/net_util.h" -#include "ui/base/text/text_elider.h" +#include "ui/gfx/text_elider.h" // Use this to get a new unique ID for a NavigationEntry during construction. // The returned ID is guaranteed to be nonzero (which is the "no ID" indicator). @@ -48,6 +48,7 @@ NavigationEntryImpl::NavigationEntryImpl() post_id_(-1), restore_type_(RESTORE_NONE), is_overriding_user_agent_(false), + http_status_code_(0), is_renderer_initiated_(false), should_replace_entry_(false), should_clear_history_list_(false), @@ -75,6 +76,7 @@ NavigationEntryImpl::NavigationEntryImpl(SiteInstanceImpl* instance, post_id_(-1), restore_type_(RESTORE_NONE), is_overriding_user_agent_(false), + http_status_code_(0), is_renderer_initiated_(is_renderer_initiated), should_replace_entry_(false), should_clear_history_list_(false), @@ -189,7 +191,7 @@ const string16& NavigationEntryImpl::GetTitleForDisplay( title = title.substr(slashpos + 1); } - ui::ElideString(title, kMaxTitleChars, &cached_display_title_); + gfx::ElideString(title, kMaxTitleChars, &cached_display_title_); return cached_display_title_; } @@ -277,6 +279,14 @@ base::Time NavigationEntryImpl::GetTimestamp() const { return timestamp_; } +void NavigationEntryImpl::SetHttpStatusCode(int http_status_code) { + http_status_code_ = http_status_code; +} + +int NavigationEntryImpl::GetHttpStatusCode() const { + return http_status_code_; +} + void NavigationEntryImpl::SetCanLoadLocalResources(bool allow) { can_load_local_resources_ = allow; } diff --git a/chromium/content/browser/web_contents/navigation_entry_impl.h b/chromium/content/browser/web_contents/navigation_entry_impl.h index 96d7ac9e2ba..63924b8ad73 100644 --- a/chromium/content/browser/web_contents/navigation_entry_impl.h +++ b/chromium/content/browser/web_contents/navigation_entry_impl.h @@ -84,6 +84,8 @@ class CONTENT_EXPORT NavigationEntryImpl virtual bool GetExtraData(const std::string& key, string16* data) const OVERRIDE; virtual void ClearExtraData(const std::string& key) OVERRIDE; + virtual void SetHttpStatusCode(int http_status_code) OVERRIDE; + virtual int GetHttpStatusCode() const OVERRIDE; void set_unique_id(int unique_id) { unique_id_ = unique_id; @@ -231,6 +233,7 @@ class CONTENT_EXPORT NavigationEntryImpl GURL original_request_url_; bool is_overriding_user_agent_; base::Time timestamp_; + int http_status_code_; // This member is not persisted with session restore because it is transient. // If the post request succeeds, this field is cleared since the same diff --git a/chromium/content/browser/web_contents/render_view_host_manager.cc b/chromium/content/browser/web_contents/render_view_host_manager.cc index 9d29a09c6a3..f8b3f06dd27 100644 --- a/chromium/content/browser/web_contents/render_view_host_manager.cc +++ b/chromium/content/browser/web_contents/render_view_host_manager.cc @@ -24,6 +24,7 @@ #include "content/public/browser/content_browser_client.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents_view.h" #include "content/public/browser/web_ui_controller.h" @@ -83,7 +84,7 @@ void RenderViewHostManager::Init(BrowserContext* browser_context, render_view_host_ = static_cast<RenderViewHostImpl*>( RenderViewHostFactory::Create( site_instance, render_view_delegate_, render_widget_delegate_, - routing_id, main_frame_routing_id, false)); + routing_id, main_frame_routing_id, false, delegate_->IsHidden())); // Keep track of renderer processes as they start to shut down or are // crashed/killed. @@ -200,6 +201,10 @@ bool RenderViewHostManager::ShouldCloseTabOnUnresponsiveRenderer() { if (!cross_navigation_pending_) return true; + // We should always have a pending RVH when there's a cross-process navigation + // in progress. Sanity check this for http://crbug.com/276333. + CHECK(pending_render_view_host_); + // If the tab becomes unresponsive during {before}unload while doing a // cross-site navigation, proceed with the navigation. (This assumes that // the pending RenderViewHost is still responsive.) @@ -382,6 +387,14 @@ void RenderViewHostManager::SwapOutOldPage() { return; DCHECK(pending_render_view_host_); + // First close any modal dialogs that would prevent us from swapping out. + // TODO(creis): This is not a guarantee. The renderer could immediately + // create another dialog in a loop, potentially causing a renderer crash when + // we tell it to swap out with a nested message loop and PageGroupLoadDeferrer + // on the stack. We should prevent the renderer from showing more dialogs + // until the SwapOut. See http://crbug.com/312490. + delegate_->CancelModalDialogsForRenderManager(); + // Tell the old renderer it is being swapped out. This will fire the unload // handler (without firing the beforeunload handler a second time). When the // unload handler finishes and the navigation completes, we will send a @@ -642,8 +655,10 @@ SiteInstance* RenderViewHostManager::GetSiteInstanceForEntry( int RenderViewHostManager::CreateRenderView( SiteInstance* instance, int opener_route_id, - bool swapped_out) { + bool swapped_out, + bool hidden) { CHECK(instance); + DCHECK(!swapped_out || hidden); // Swapped out views should always be hidden. // Check if we've already created an RVH for this SiteInstance. If so, try // to re-use the existing one, which has already been initialized. We'll @@ -662,7 +677,8 @@ int RenderViewHostManager::CreateRenderView( render_widget_delegate_, MSG_ROUTING_NONE, MSG_ROUTING_NONE, - swapped_out)); + swapped_out, + hidden)); // If the new RVH is swapped out already, store it. Otherwise prevent the // process from exiting while we're trying to navigate in it. @@ -690,9 +706,9 @@ int RenderViewHostManager::CreateRenderView( bool RenderViewHostManager::InitRenderView(RenderViewHost* render_view_host, int opener_route_id) { - // If the pending navigation is to a WebUI, tell the RenderView about any - // bindings it will need enabled. - if (pending_web_ui()) + // If the pending navigation is to a WebUI and the RenderView is not in a + // guest process, tell the RenderView about any bindings it will need enabled. + if (pending_web_ui() && !render_view_host->GetProcess()->IsGuest()) render_view_host->AllowBindings(pending_web_ui()->GetBindings()); return delegate_->CreateRenderViewForRenderManager(render_view_host, @@ -706,6 +722,14 @@ void RenderViewHostManager::CommitPending() { // this triggers won't be able to figure out what's going on. bool will_focus_location_bar = delegate_->FocusLocationBarByDefault(); + // We currently can't guarantee that the renderer isn't showing a new modal + // dialog, even though we canceled them in SwapOutOldPage. (It may have + // created another in the meantime.) Make sure we run and reset the callback + // now before we delete its RVH below. + // TODO(creis): Remove this if we can guarantee that no new dialogs will be + // shown after SwapOutOldPage. See http://crbug.com/312490. + delegate_->CancelModalDialogsForRenderManager(); + // Next commit the Web UI, if any. Either replace |web_ui_| with // |pending_web_ui_|, or clear |web_ui_| if there is no pending WebUI, or // leave |web_ui_| as is if reusing it. @@ -740,10 +764,10 @@ void RenderViewHostManager::CommitPending() { // If the view is gone, then this RenderViewHost died while it was hidden. // We ignored the RenderProcessGone call at the time, so we should send it now // to make sure the sad tab shows up, etc. - if (render_view_host_->GetView()) - render_view_host_->GetView()->Show(); - else + if (!render_view_host_->GetView()) delegate_->RenderProcessGoneFromRenderManager(render_view_host_); + else if (!delegate_->IsHidden()) + render_view_host_->GetView()->Show(); // Hide the old view now that the new one is visible. if (old_render_view_host->GetView()) { @@ -816,33 +840,16 @@ void RenderViewHostManager::ShutdownRenderViewHostsInSiteInstance( // list. swapped_out_hosts_.erase(site_instance_id); - RenderWidgetHost::List widgets = - RenderWidgetHostImpl::GetAllRenderWidgetHosts(); - - // Here deleting a RWH in widgets can possibly cause another RWH in - // the list to be deleted. This can result in leaving a dangling - // pointer in the widgets list. Our assumption is that a widget - // deleted as that sort of side-effect should not be directly - // deleted here. Therefore, we first gather only widgets directly to - // be deleted so that we don't hit any future dangling pointers in - // widgets. - std::vector<RenderViewHostImpl*> rvhs_to_be_deleted; - - for (size_t i = 0; i < widgets.size(); ++i) { - if (!widgets[i]->IsRenderView()) + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHostImpl::GetAllRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { + if (!widget->IsRenderView()) continue; RenderViewHostImpl* rvh = - static_cast<RenderViewHostImpl*>(RenderViewHost::From(widgets[i])); - if (site_instance_id == rvh->GetSiteInstance()->GetId()) { - DCHECK(rvh->is_swapped_out()); - rvhs_to_be_deleted.push_back(rvh); - } + static_cast<RenderViewHostImpl*>(RenderViewHost::From(widget)); + if (site_instance_id == rvh->GetSiteInstance()->GetId()) + rvh->Shutdown(); } - - // Finally we delete the gathered RVHs, which should not indirectly - // delete each other. - for (size_t i = 0; i < rvhs_to_be_deleted.size(); ++i) - rvhs_to_be_deleted[i]->Shutdown(); } RenderViewHostImpl* RenderViewHostManager::UpdateRendererStateForNavigate( @@ -894,7 +901,8 @@ RenderViewHostImpl* RenderViewHostManager::UpdateRendererStateForNavigate( // Create a non-swapped-out pending RVH with the given opener and navigate // it. - int route_id = CreateRenderView(new_instance, opener_route_id, false); + int route_id = CreateRenderView(new_instance, opener_route_id, false, + delegate_->IsHidden()); if (route_id == MSG_ROUTING_NONE) return NULL; diff --git a/chromium/content/browser/web_contents/render_view_host_manager.h b/chromium/content/browser/web_contents/render_view_host_manager.h index 8c5071d9d65..8f73610b4b5 100644 --- a/chromium/content/browser/web_contents/render_view_host_manager.h +++ b/chromium/content/browser/web_contents/render_view_host_manager.h @@ -64,6 +64,7 @@ class CONTENT_EXPORT RenderViewHostManager virtual void RenderProcessGoneFromRenderManager( RenderViewHost* render_view_host) = 0; virtual void UpdateRenderViewSizeForRenderManager() = 0; + virtual void CancelModalDialogsForRenderManager() = 0; virtual void NotifySwappedFromRenderManager( RenderViewHost* old_render_view_host) = 0; virtual NavigationControllerImpl& @@ -97,6 +98,10 @@ class CONTENT_EXPORT RenderViewHostManager // Creates a view and sets the size for the specified RVH. virtual void CreateViewAndSetSizeForRVH(RenderViewHost* rvh) = 0; + // Returns true if views created for this delegate should be created in a + // hidden state. + virtual bool IsHidden() = 0; + protected: virtual ~Delegate() {} }; @@ -181,7 +186,8 @@ class CONTENT_EXPORT RenderViewHostManager // will be used for a pending cross-site navigation. int CreateRenderView(SiteInstance* instance, int opener_route_id, - bool swapped_out); + bool swapped_out, + bool hidden); // Called when a provisional load on the given renderer is aborted. void RendererAbortedProvisionalLoad(RenderViewHost* render_view_host); diff --git a/chromium/content/browser/web_contents/render_view_host_manager_unittest.cc b/chromium/content/browser/web_contents/render_view_host_manager_unittest.cc index ea35e6ba05f..d8837ebc5cc 100644 --- a/chromium/content/browser/web_contents/render_view_host_manager_unittest.cc +++ b/chromium/content/browser/web_contents/render_view_host_manager_unittest.cc @@ -16,6 +16,7 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host_observer.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/web_ui_controller.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/javascript_message_type.h" @@ -345,12 +346,14 @@ TEST_F(RenderViewHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) { TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); EXPECT_TRUE(swapped_out_rvh->is_swapped_out()); - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); // We know that there is the only one active widget. Another view is // now swapped out, so the swapped out view is not included in the // list. - EXPECT_TRUE(widgets.size() == 1); - RenderViewHost* rvh = RenderViewHost::From(widgets[0]); + RenderWidgetHost* widget = widgets->GetNextHost(); + EXPECT_FALSE(widgets->GetNextHost()); + RenderViewHost* rvh = RenderViewHost::From(widget); EXPECT_FALSE(static_cast<RenderViewHostImpl*>(rvh)->is_swapped_out()); } @@ -364,14 +367,15 @@ TEST_F(RenderViewHostManagerTest, TestRenderViewHost* swapped_out_rvh = CreateSwappedOutRenderViewHost(); EXPECT_TRUE(swapped_out_rvh->is_swapped_out()); - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - RenderWidgetHost::List all_widgets = - RenderWidgetHostImpl::GetAllRenderWidgetHosts(); + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); - for (size_t i = 0; i < widgets.size(); ++i) { + while (RenderWidgetHost* w = widgets->GetNextHost()) { bool found = false; - for (size_t j = 0; j < all_widgets.size(); ++j) { - if (widgets[i] == all_widgets[j]) { + scoped_ptr<RenderWidgetHostIterator> all_widgets( + RenderWidgetHostImpl::GetAllRenderWidgetHosts()); + while (RenderWidgetHost* widget = all_widgets->GetNextHost()) { + if (w == widget) { found = true; break; } diff --git a/chromium/content/browser/web_contents/touch_editable_impl_aura.cc b/chromium/content/browser/web_contents/touch_editable_impl_aura.cc index d38f5f5c7f6..ada5976aa7b 100644 --- a/chromium/content/browser/web_contents/touch_editable_impl_aura.cc +++ b/chromium/content/browser/web_contents/touch_editable_impl_aura.cc @@ -14,8 +14,8 @@ #include "ui/aura/root_window.h" #include "ui/aura/window.h" #include "ui/base/clipboard/clipboard.h" -#include "ui/base/range/range.h" #include "ui/base/ui_base_switches_util.h" +#include "ui/gfx/range/range.h" namespace content { @@ -295,7 +295,7 @@ bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const { if (!rwhva_) return false; bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE; - ui::Range selection_range; + gfx::Range selection_range; rwhva_->GetSelectionRange(&selection_range); bool has_selection = !selection_range.is_empty(); switch (command_id) { diff --git a/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc b/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc index ce0a7027c12..6c5fa2a1bbf 100644 --- a/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc +++ b/chromium/content/browser/web_contents/touch_editable_impl_aura_browsertest.cc @@ -16,18 +16,19 @@ #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "ui/aura/root_window.h" #include "ui/aura/test/event_generator.h" #include "ui/aura/window.h" -#include "ui/base/events/event_utils.h" #include "ui/base/ui_base_switches.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/events/event_utils.h" namespace content { +// TODO(mohsen): Remove logs if the test showed no flakiness anymore. class TestTouchEditableImplAura : public TouchEditableImplAura { public: TestTouchEditableImplAura() @@ -37,28 +38,61 @@ class TestTouchEditableImplAura : public TouchEditableImplAura { waiting_for_gesture_ack_callback_(false) {} void Reset() { + LOG(INFO) << "TestTouchEditableImplAura::Reset()"; selection_changed_callback_arrived_ = false; waiting_for_selection_changed_callback_ = false; gesture_ack_callback_arrived_ = false; waiting_for_gesture_ack_callback_ = false; } + virtual void StartTouchEditing() OVERRIDE { + LOG(INFO) << "TestTouchEditableImplAura::StartTouchEditing()"; + TouchEditableImplAura::StartTouchEditing(); + } + + virtual void EndTouchEditing() OVERRIDE { + LOG(INFO) << "TestTouchEditableImplAura::EndTouchEditing()"; + TouchEditableImplAura::EndTouchEditing(); + } + virtual void OnSelectionOrCursorChanged(const gfx::Rect& anchor, const gfx::Rect& focus) OVERRIDE { + LOG(INFO) << "TestTouchEditableImplAura::OnSelectionOrCursorChanged(" + << anchor.ToString() << ", " << focus.ToString() << ")"; selection_changed_callback_arrived_ = true; TouchEditableImplAura::OnSelectionOrCursorChanged(anchor, focus); if (waiting_for_selection_changed_callback_) selection_changed_wait_run_loop_->Quit(); } + virtual void OnTextInputTypeChanged(ui::TextInputType type) OVERRIDE { + LOG(INFO) << "TestTouchEditableImplAura::OnTextInputTypeChanged(" + << type << ")"; + TouchEditableImplAura::OnTextInputTypeChanged(type); + } + + virtual bool HandleInputEvent(const ui::Event* event) OVERRIDE { + LOG(INFO) << "TestTouchEditableImplAura::HandleInputEvent(" + << event->type() << ")"; + return TouchEditableImplAura::HandleInputEvent(event); + } + virtual void GestureEventAck(int gesture_event_type) OVERRIDE { + LOG(INFO) << "TestTouchEditableImplAura::GestureEventAck(" + << gesture_event_type << ")"; gesture_ack_callback_arrived_ = true; TouchEditableImplAura::GestureEventAck(gesture_event_type); if (waiting_for_gesture_ack_callback_) gesture_ack_wait_run_loop_->Quit(); } + virtual void OnViewDestroyed() OVERRIDE { + LOG(INFO) << "TestTouchEditableImplAura::OnViewDestroyed()"; + TouchEditableImplAura::OnViewDestroyed(); + } + void WaitForSelectionChangeCallback() { + LOG(INFO) << "TestTouchEditableImplAura::WaitForSelectionChangeCallback()"; if (selection_changed_callback_arrived_) return; waiting_for_selection_changed_callback_ = true; @@ -67,6 +101,7 @@ class TestTouchEditableImplAura : public TouchEditableImplAura { } void WaitForGestureAck() { + LOG(INFO) << "TestTouchEditableImplAura::WaitForGestureAck()"; if (gesture_ack_callback_arrived_) return; waiting_for_gesture_ack_callback_ = true; @@ -115,6 +150,7 @@ class TouchEditableImplAuraTest : public ContentBrowserTest { content->GetRootWindow()->SetHostSize(gfx::Size(800, 600)); } + // TODO(mohsen): Remove logs if the test showed no flakiness anymore. void TestTouchSelectionOriginatingFromWebpage() { ASSERT_NO_FATAL_FAILURE( StartTestWithPage("files/touch_selection.html")); @@ -132,30 +168,40 @@ class TouchEditableImplAuraTest : public ContentBrowserTest { aura::test::EventGenerator generator(content->GetRootWindow(), content); gfx::Rect bounds = content->GetBoundsInRootWindow(); + LOG(INFO) << "Select text and wait for selection change."; touch_editable->Reset(); ExecuteSyncJSFunction(view_host, "select_all_text()"); touch_editable->WaitForSelectionChangeCallback(); + LOG(INFO) << "Tap on selection to bring up handles."; // Tap inside selection to bring up selection handles. generator.GestureTapAt(gfx::Point(bounds.x() + 10, bounds.y() + 10)); EXPECT_EQ(touch_editable->rwhva_, rwhva); + LOG(INFO) << "Get selection."; scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(view_host, "get_selection()"); std::string selection; value->GetAsString(&selection); + LOG(INFO) << "Test handles and selection."; // Check if selection handles are showing. EXPECT_TRUE(touch_editable->touch_selection_controller_.get()); EXPECT_STREQ("Some text we can select", selection.c_str()); + LOG(INFO) << "Drag handles to modify the selection."; // Lets move the handles a bit to modify the selection touch_editable->Reset(); generator.GestureScrollSequence( gfx::Point(10, 47), gfx::Point(30, 47), base::TimeDelta::FromMilliseconds(20), - 1); + 5); + LOG(INFO) << "Handle moved. Now, waiting for selection to change."; + touch_editable->WaitForSelectionChangeCallback(); + LOG(INFO) << "Selection changed."; + + LOG(INFO) << "Test selection."; EXPECT_TRUE(touch_editable->touch_selection_controller_.get()); value = content::ExecuteScriptAndGetValue(view_host, "get_selection()"); value->GetAsString(&selection); @@ -275,6 +321,7 @@ class TouchEditableImplAuraTest : public ContentBrowserTest { EXPECT_TRUE(touch_editable->touch_selection_controller_.get()); } + // TODO(mohsen): Remove logs if the test showed no flakiness anymore. void TestTouchCursorInTextfield() { ASSERT_NO_FATAL_FAILURE( StartTestWithPage("files/touch_selection.html")); @@ -292,21 +339,32 @@ class TouchEditableImplAuraTest : public ContentBrowserTest { aura::test::EventGenerator generator(content->GetRootWindow(), content); gfx::Rect bounds = content->GetBoundsInRootWindow(); EXPECT_EQ(touch_editable->rwhva_, rwhva); + + LOG(INFO) << "Focus the textfield."; ExecuteSyncJSFunction(view_host, "focus_textfield()"); + LOG(INFO) << "Wait for selection to change."; + touch_editable->WaitForSelectionChangeCallback(); // Tap textfield touch_editable->Reset(); + LOG(INFO) << "Tap in the textfield."; generator.GestureTapAt(gfx::Point(bounds.x() + 50, bounds.y() + 40)); + LOG(INFO) << "Wait for selection to change."; + touch_editable->WaitForSelectionChangeCallback(); + LOG(INFO) << "Wait for tap-down ACK."; touch_editable->WaitForGestureAck(); // Wait for Tap Down Ack touch_editable->Reset(); + LOG(INFO) << "Wait for tap ACK."; touch_editable->WaitForGestureAck(); // Wait for Tap Ack. + LOG(INFO) << "Test the touch selection handle."; // Check if cursor handle is showing. ui::TouchSelectionController* controller = touch_editable->touch_selection_controller_.get(); EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, touch_editable->text_input_type_); EXPECT_TRUE(controller); + LOG(INFO) << "Test cursor position."; scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(view_host, "get_cursor_position()"); int cursor_pos = -1; @@ -314,14 +372,18 @@ class TouchEditableImplAuraTest : public ContentBrowserTest { EXPECT_NE(-1, cursor_pos); // Move the cursor handle. + LOG(INFO) << "Drag the touch selection handle to change its position."; generator.GestureScrollSequence( gfx::Point(50, 59), gfx::Point(10, 59), base::TimeDelta::FromMilliseconds(20), 1); + LOG(INFO) << "Wait for cursor position to change."; + touch_editable->WaitForSelectionChangeCallback(); + LOG(INFO) << "Check cursor position is changed."; EXPECT_TRUE(touch_editable->touch_selection_controller_.get()); - value = content::ExecuteScriptAndGetValue( - view_host, "get_cursor_position()"); + value = content::ExecuteScriptAndGetValue(view_host, + "get_cursor_position()"); int new_cursor_pos = -1; value->GetAsInteger(&new_cursor_pos); EXPECT_NE(-1, new_cursor_pos); @@ -348,9 +410,8 @@ IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest, TestTouchSelectionOnLongPress(); } -// TODO(miu): Disabled test due to flakiness. http://crbug.com/235991 IN_PROC_BROWSER_TEST_F(TouchEditableImplAuraTest, - DISABLED_TouchCursorInTextfieldTest) { + TouchCursorInTextfieldTest) { TestTouchCursorInTextfield(); } diff --git a/chromium/content/browser/web_contents/web_contents_drag_win.cc b/chromium/content/browser/web_contents/web_contents_drag_win.cc index 68e27884475..cb24210c3b9 100644 --- a/chromium/content/browser/web_contents/web_contents_drag_win.cc +++ b/chromium/content/browser/web_contents/web_contents_drag_win.cc @@ -163,7 +163,7 @@ void WebContentsDragWin::StartDragging(const DropData& drop_data, drag_source_ = new WebDragSource(source_window_, web_contents_); - const GURL& page_url = web_contents_->GetURL(); + const GURL& page_url = web_contents_->GetLastCommittedURL(); const std::string& page_encoding = web_contents_->GetEncoding(); // If it is not drag-out, do the drag-and-drop in the current UI thread. diff --git a/chromium/content/browser/web_contents/web_contents_impl.cc b/chromium/content/browser/web_contents/web_contents_impl.cc index 2d19b65e479..26e6baa746d 100644 --- a/chromium/content/browser/web_contents/web_contents_impl.cc +++ b/chromium/content/browser/web_contents/web_contents_impl.cc @@ -29,6 +29,7 @@ #include "content/browser/download/download_stats.h" #include "content/browser/download/mhtml_generation_manager.h" #include "content/browser/download/save_package.h" +#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/host_zoom_map_impl.h" @@ -53,7 +54,6 @@ #include "content/port/browser/render_widget_host_view_port.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/color_chooser.h" -#include "content/public/browser/compositor_util.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/download_manager.h" @@ -79,6 +79,9 @@ #include "net/base/mime_util.h" #include "net/base/net_util.h" #include "net/base/network_change_notifier.h" +#include "net/http/http_cache.h" +#include "net/http/http_transaction_factory.h" +#include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "ui/base/layout.h" #include "ui/base/touch/touch_device.h" @@ -162,15 +165,13 @@ g_created_callbacks = LAZY_INSTANCE_INITIALIZER; static int StartDownload(content::RenderViewHost* rvh, const GURL& url, bool is_favicon, - uint32_t preferred_image_size, - uint32_t max_image_size) { + uint32_t max_bitmap_size) { static int g_next_image_download_id = 0; rvh->Send(new ImageMsg_DownloadImage(rvh->GetRoutingID(), ++g_next_image_download_id, url, is_favicon, - preferred_image_size, - max_image_size)); + max_bitmap_size)); return g_next_image_download_id; } @@ -265,6 +266,14 @@ void MakeNavigateParams(const NavigationEntryImpl& entry, delegate->AddNavigationHeaders(params->url, ¶ms->extra_headers); } +void NotifyCacheOnIO( + scoped_refptr<net::URLRequestContextGetter> request_context, + const GURL& url, + const std::string& http_method) { + request_context->GetURLRequestContext()->http_transaction_factory()-> + GetCache()->OnExternalCacheHit(url, http_method); +} + } // namespace WebContents* WebContents::Create(const WebContents::CreateParams& params) { @@ -554,8 +563,11 @@ WebPreferences WebContentsImpl::GetWebkitPrefs(RenderViewHost* rvh, } #if defined(OS_ANDROID) + prefs.use_solid_color_scrollbars = true; prefs.user_gesture_required_for_media_playback = !command_line.HasSwitch( switches::kDisableGestureRequirementForMediaPlayback); + prefs.user_gesture_required_for_media_fullscreen = !command_line.HasSwitch( + switches::kDisableGestureRequirementForMediaFullscreen); #endif prefs.touch_enabled = ui::AreTouchEventsEnabled(); @@ -834,6 +846,14 @@ RenderWidgetHostViewPort* WebContentsImpl::GetRenderWidgetHostViewPort() const { return RenderWidgetHostViewPort::FromRWHV(GetRenderWidgetHostView()); } +RenderWidgetHostView* WebContentsImpl::GetFullscreenRenderWidgetHostView() + const { + RenderWidgetHost* const widget_host = + RenderWidgetHostImpl::FromID(GetRenderProcessHost()->GetID(), + GetFullscreenWidgetRoutingID()); + return widget_host ? widget_host->GetView() : NULL; +} + WebContentsView* WebContentsImpl::GetView() const { return view_.get(); } @@ -1049,9 +1069,7 @@ void WebContentsImpl::DecrementCapturerCount() { if (is_being_destroyed_) return; - // While capturer_count_ was greater than zero, the WasHidden() calls to RWHV - // were being prevented. If there are no more capturers, make the call now. - if (capturer_count_ == 0 && !should_normally_be_visible_) { + if (IsHidden()) { DVLOG(1) << "Executing delayed WasHidden()."; WasHidden(); } @@ -1099,7 +1117,7 @@ void WebContentsImpl::WasShown() { RenderWidgetHostViewPort* rwhv = RenderWidgetHostViewPort::FromRWHV(GetRenderWidgetHostView()); if (rwhv) { - rwhv->WasShown(); + rwhv->Show(); #if defined(OS_MACOSX) rwhv->SetActive(true); #endif @@ -1137,7 +1155,7 @@ void WebContentsImpl::WasHidden() { RenderWidgetHostViewPort* rwhv = RenderWidgetHostViewPort::FromRWHV(GetRenderWidgetHostView()); if (rwhv) - rwhv->WasHidden(); + rwhv->Hide(); } should_normally_be_visible_ = false; @@ -1157,7 +1175,7 @@ bool WebContentsImpl::NeedToFireBeforeUnload() { void WebContentsImpl::Stop() { render_manager_.Stop(); - FOR_EACH_OBSERVER(WebContentsObserver, observers_, StopNavigation()); + FOR_EACH_OBSERVER(WebContentsObserver, observers_, NavigationStopped()); } WebContents* WebContentsImpl::Clone() { @@ -1195,6 +1213,10 @@ void WebContentsImpl::Observe(int type, } void WebContentsImpl::Init(const WebContents::CreateParams& params) { + // This is set before initializing the render_manager_ since render_manager_ + // init calls back into us via its delegate to ask if it should be hidden. + should_normally_be_visible_ = !params.initially_hidden; + render_manager_.Init( params.browser_context, params.site_instance, params.routing_id, params.main_frame_routing_id); @@ -1318,6 +1340,8 @@ void WebContentsImpl::RenderWidgetDeleted( if (render_widget_host && render_widget_host->GetRoutingID() == fullscreen_widget_routing_id_) { + if (delegate_ && delegate_->EmbedsFullscreenWidget()) + delegate_->ToggleFullscreenModeForTab(this, false); FOR_EACH_OBSERVER(WebContentsObserver, observers_, DidDestroyFullscreenWidget( @@ -1395,6 +1419,13 @@ void WebContentsImpl::HandleGestureEnd() { } void WebContentsImpl::ToggleFullscreenMode(bool enter_fullscreen) { + // This method is being called to enter or leave renderer-initiated fullscreen + // mode. Either way, make sure any existing fullscreen widget is shut down + // first. + RenderWidgetHostView* const widget_view = GetFullscreenRenderWidgetHostView(); + if (widget_view) + RenderWidgetHostImpl::From(widget_view->GetRenderWidgetHost())->Shutdown(); + if (delegate_) delegate_->ToggleFullscreenModeForTab(this, enter_fullscreen); } @@ -1422,23 +1453,6 @@ void WebContentsImpl::CreateNewWindow( int main_frame_route_id, const ViewHostMsg_CreateWindow_Params& params, SessionStorageNamespace* session_storage_namespace) { - if (delegate_ && - !delegate_->ShouldCreateWebContents(this, - route_id, - params.window_container_type, - params.frame_name, - params.target_url, - params.referrer, - params.disposition, - params.features, - params.user_gesture, - params.opener_suppressed)) { - GetRenderViewHost()->GetProcess()->ResumeRequestsForView(route_id); - GetRenderViewHost()->GetProcess()->ResumeRequestsForView( - main_frame_route_id); - return; - } - // We usually create the new window in the same BrowsingInstance (group of // script-related windows), by passing in the current SiteInstance. However, // if the opener is being suppressed (in a non-guest), we create a new @@ -1450,12 +1464,6 @@ void WebContentsImpl::CreateNewWindow( SiteInstance::CreateForURL(GetBrowserContext(), params.target_url) : GetSiteInstance(); - // Create the new web contents. This will automatically create the new - // WebContentsView. In the future, we may want to create the view separately. - WebContentsImpl* new_contents = - new WebContentsImpl(GetBrowserContext(), - params.opener_suppressed ? NULL : this); - // We must assign the SessionStorageNamespace before calling Init(). // // http://crbug.com/142685 @@ -1470,6 +1478,27 @@ void WebContentsImpl::CreateNewWindow( SessionStorageNamespaceImpl* session_storage_namespace_impl = static_cast<SessionStorageNamespaceImpl*>(session_storage_namespace); CHECK(session_storage_namespace_impl->IsFromContext(dom_storage_context)); + + if (delegate_ && + !delegate_->ShouldCreateWebContents(this, + route_id, + params.window_container_type, + params.frame_name, + params.target_url, + partition_id, + session_storage_namespace)) { + GetRenderViewHost()->GetProcess()->ResumeRequestsForView(route_id); + GetRenderViewHost()->GetProcess()->ResumeRequestsForView( + main_frame_route_id); + return; + } + + // Create the new web contents. This will automatically create the new + // WebContentsView. In the future, we may want to create the view separately. + WebContentsImpl* new_contents = + new WebContentsImpl(GetBrowserContext(), + params.opener_suppressed ? NULL : this); + new_contents->GetController().SetSessionStorageNamespace( partition_id, session_storage_namespace); @@ -1488,6 +1517,8 @@ void WebContentsImpl::CreateNewWindow( BrowserPluginGuest::CreateWithOpener(instance_id, new_contents_impl, GetBrowserPluginGuest(), !!new_contents_impl->opener()); } + if (params.disposition == NEW_BACKGROUND_TAB) + create_params.initially_hidden = true; new_contents->Init(create_params); // Save the window for later if we're not suppressing the opener (since it @@ -1549,7 +1580,7 @@ void WebContentsImpl::CreateNewWidget(int route_id, WebKit::WebPopupType popup_type) { RenderProcessHost* process = GetRenderProcessHost(); RenderWidgetHostImpl* widget_host = - new RenderWidgetHostImpl(this, process, route_id); + new RenderWidgetHostImpl(this, process, route_id, IsHidden()); created_widgets_.insert(widget_host); RenderWidgetHostViewPort* widget_view = RenderWidgetHostViewPort::FromRWHV( @@ -1591,12 +1622,6 @@ void WebContentsImpl::ShowCreatedWidget(int route_id, void WebContentsImpl::ShowCreatedFullscreenWidget(int route_id) { ShowCreatedWidget(route_id, true, gfx::Rect()); - - DCHECK_EQ(MSG_ROUTING_NONE, fullscreen_widget_routing_id_); - fullscreen_widget_routing_id_ = route_id; - FOR_EACH_OBSERVER(WebContentsObserver, - observers_, - DidShowFullscreenWidget(route_id)); } void WebContentsImpl::ShowCreatedWidget(int route_id, @@ -1609,17 +1634,35 @@ void WebContentsImpl::ShowCreatedWidget(int route_id, RenderWidgetHostViewPort::FromRWHV(GetCreatedWidget(route_id)); if (!widget_host_view) return; - if (is_fullscreen) - widget_host_view->InitAsFullscreen(GetRenderWidgetHostViewPort()); - else + bool allow_privileged = false; + if (is_fullscreen) { + if (delegate_ && delegate_->EmbedsFullscreenWidget()) { + widget_host_view->InitAsChild(GetRenderWidgetHostView()->GetNativeView()); + delegate_->ToggleFullscreenModeForTab(this, true); + } else { + widget_host_view->InitAsFullscreen(GetRenderWidgetHostViewPort()); + // Only allow privileged mouse lock for fullscreen render widget, which is + // used to implement Pepper Flash fullscreen. + allow_privileged = true; + } + + DCHECK_EQ(MSG_ROUTING_NONE, fullscreen_widget_routing_id_); + fullscreen_widget_routing_id_ = route_id; + FOR_EACH_OBSERVER(WebContentsObserver, + observers_, + DidShowFullscreenWidget(route_id)); + if (!widget_host_view->HasFocus()) + widget_host_view->Focus(); + } else { widget_host_view->InitAsPopup(GetRenderWidgetHostViewPort(), initial_pos); + } RenderWidgetHostImpl* render_widget_host_impl = RenderWidgetHostImpl::From(widget_host_view->GetRenderWidgetHost()); render_widget_host_impl->Init(); - // Only allow privileged mouse lock for fullscreen render widget, which is - // used to implement Pepper Flash fullscreen. - render_widget_host_impl->set_allow_privileged_mouse_lock(is_fullscreen); + render_widget_host_impl->set_allow_privileged_mouse_lock(allow_privileged); + // TODO(miu): For now, all mouse lock requests by embedded Flash fullscreen + // will be denied. This is to be rectified in a soon-upcoming change. #if defined(OS_MACOSX) // A RenderWidgetHostViewMac has lifetime scoped to the view. Now that it's @@ -1746,6 +1789,12 @@ void WebContentsImpl::AttachInterstitialPage( InterstitialPageImpl* interstitial_page) { DCHECK(interstitial_page); render_manager_.set_interstitial_page(interstitial_page); + + // Cancel any visible dialogs so that they don't interfere with the + // interstitial. + if (dialog_manager_) + dialog_manager_->CancelActiveAndPendingDialogs(this); + FOR_EACH_OBSERVER(WebContentsObserver, observers_, DidAttachInterstitialPage()); } @@ -1764,8 +1813,11 @@ bool WebContentsImpl::NavigateToEntry( // The renderer will reject IPC messages with URLs longer than // this limit, so don't attempt to navigate with a longer URL. - if (entry.GetURL().spec().size() > kMaxURLChars) + if (entry.GetURL().spec().size() > kMaxURLChars) { + LOG(WARNING) << "Refusing to load URL as it exceeds " << kMaxURLChars + << " characters."; return false; + } RenderViewHostImpl* dest_render_view_host = static_cast<RenderViewHostImpl*>(render_manager_.Navigate(entry)); @@ -1806,7 +1858,7 @@ bool WebContentsImpl::NavigateToEntry( // do not generate content. What we really need is a message from the // renderer telling us that a new page was not created. The same message // could be used for mailto: URLs and the like. - if (entry.GetURL().SchemeIs(chrome::kJavaScriptScheme)) + if (entry.GetURL().SchemeIs(kJavaScriptScheme)) return false; } @@ -1931,8 +1983,8 @@ void WebContentsImpl::SaveFrame(const GURL& url, void WebContentsImpl::GenerateMHTML( const base::FilePath& file, - const base::Callback<void(const base::FilePath&, int64)>& callback) { - MHTMLGenerationManager::GetInstance()->GenerateMHTML(this, file, callback); + const base::Callback<void(int64)>& callback) { + MHTMLGenerationManager::GetInstance()->SaveMHTML(this, file, callback); } bool WebContentsImpl::IsActiveEntry(int32 page_id) { @@ -2098,12 +2150,10 @@ void WebContentsImpl::DidEndColorChooser() { int WebContentsImpl::DownloadImage(const GURL& url, bool is_favicon, - uint32_t preferred_image_size, - uint32_t max_image_size, + uint32_t max_bitmap_size, const ImageDownloadCallback& callback) { RenderViewHost* host = GetRenderViewHost(); - int id = StartDownload( - host, url, is_favicon, preferred_image_size, max_image_size); + int id = StartDownload(host, url, is_favicon, max_bitmap_size); image_download_map_[id] = callback; return id; } @@ -2155,6 +2205,13 @@ void WebContentsImpl::DidStartProvisionalLoadForFrame( GetBrowserContext())); entry->set_site_instance( static_cast<SiteInstanceImpl*>(GetSiteInstance())); + // TODO(creis): If there's a pending entry already, find a safe way to + // update it instead of replacing it and copying over things like this. + if (pending_entry && + NavigationEntryImpl::FromNavigationEntry(pending_entry)-> + should_replace_entry()) { + entry->set_should_replace_entry(true); + } controller_.SetPendingEntry(entry); NotifyNavigationStateChanged(content::INVALIDATE_TYPE_URL); } @@ -2180,8 +2237,8 @@ void WebContentsImpl::DidRedirectProvisionalLoad( const GURL& source_url, const GURL& target_url) { // TODO(creis): Remove this method and have the pre-rendering code listen to - // the ResourceDispatcherHost's RESOURCE_RECEIVED_REDIRECT notification - // instead. See http://crbug.com/78512. + // WebContentsObserver::DidGetRedirectForResourceRequest instead. + // See http://crbug.com/78512. GURL validated_source_url(source_url); GURL validated_target_url(target_url); RenderProcessHost* render_process_host = @@ -2288,10 +2345,29 @@ void WebContentsImpl::OnDidLoadResourceFromMemoryCache( url, GetRenderProcessHost()->GetID(), cert_id, cert_status, http_method, mime_type, resource_type); + controller_.ssl_manager()->DidLoadFromMemoryCache(details); + + FOR_EACH_OBSERVER(WebContentsObserver, observers_, + DidLoadResourceFromMemoryCache(details)); + + // TODO(avi): Remove. http://crbug.com/170921 NotificationService::current()->Notify( NOTIFICATION_LOAD_FROM_MEMORY_CACHE, Source<NavigationController>(&controller_), Details<LoadFromMemoryCacheDetails>(&details)); + + if (url.is_valid() && url.SchemeIsHTTPOrHTTPS()) { + scoped_refptr<net::URLRequestContextGetter> request_context( + resource_type == ResourceType::MEDIA ? + GetBrowserContext()->GetMediaRequestContextForRenderProcess( + GetRenderProcessHost()->GetID()) : + GetBrowserContext()->GetRequestContextForRenderProcess( + GetRenderProcessHost()->GetID())); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&NotifyCacheOnIO, request_context, url, http_method)); + } } void WebContentsImpl::OnDidDisplayInsecureContent() { @@ -2438,10 +2514,20 @@ void WebContentsImpl::OnFindMatchRectsReply( void WebContentsImpl::OnOpenDateTimeDialog( const ViewHostMsg_DateTimeDialogValue_Params& value) { - date_time_chooser_->ShowDialog( - ContentViewCore::FromWebContents(this), GetRenderViewHost(), - value.dialog_type, value.year, value.month, value.day, value.hour, - value.minute, value.second, value.week, value.minimum, value.maximum); + date_time_chooser_->ShowDialog(ContentViewCore::FromWebContents(this), + GetRenderViewHost(), + value.dialog_type, + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.second, + value.milli, + value.week, + value.minimum, + value.maximum, + value.step); } #endif @@ -2537,8 +2623,8 @@ void WebContentsImpl::OnDidDownloadImage( int id, int http_status_code, const GURL& image_url, - int requested_size, - const std::vector<SkBitmap>& bitmaps) { + const std::vector<SkBitmap>& bitmaps, + const std::vector<gfx::Size>& original_bitmap_sizes) { ImageDownloadMap::iterator iter = image_download_map_.find(id); if (iter == image_download_map_.end()) { // Currently WebContents notifies us of ANY downloads so that it is @@ -2546,7 +2632,8 @@ void WebContentsImpl::OnDidDownloadImage( return; } if (!iter->second.is_null()) { - iter->second.Run(id, http_status_code, image_url, requested_size, bitmaps); + iter->second.Run( + id, http_status_code, image_url, bitmaps, original_bitmap_sizes); } image_download_map_.erase(id); } @@ -2794,6 +2881,10 @@ void WebContentsImpl::NotifySwapped(RenderViewHost* old_render_view_host) { // notification so that clients that pick up a pointer to |this| can NULL the // pointer. See Bug 1230284. notify_disconnection_ = true; + FOR_EACH_OBSERVER(WebContentsObserver, observers_, + RenderViewHostSwapped(old_render_view_host)); + + // TODO(avi): Remove. http://crbug.com/170921 NotificationService::current()->Notify( NOTIFICATION_WEB_CONTENTS_SWAPPED, Source<WebContents>(this), @@ -2805,14 +2896,8 @@ void WebContentsImpl::NotifySwapped(RenderViewHost* old_render_view_host) { RemoveBrowserPluginEmbedder(); } -void WebContentsImpl::NotifyConnected() { - notify_disconnection_ = true; - NotificationService::current()->Notify( - NOTIFICATION_WEB_CONTENTS_CONNECTED, - Source<WebContents>(this), - NotificationService::NoDetails()); -} - +// TODO(avi): Remove this entire function because this notification is already +// covered by two observer functions. http://crbug.com/170921 void WebContentsImpl::NotifyDisconnected() { if (!notify_disconnection_) return; @@ -2899,7 +2984,13 @@ void WebContentsImpl::RenderViewReady(RenderViewHost* rvh) { return; } - NotifyConnected(); + notify_disconnection_ = true; + // TODO(avi): Remove. http://crbug.com/170921 + NotificationService::current()->Notify( + NOTIFICATION_WEB_CONTENTS_CONNECTED, + Source<WebContents>(this), + NotificationService::NoDetails()); + bool was_crashed = IsCrashed(); SetIsCrashed(base::TERMINATION_STATUS_STILL_RUNNING, 0); @@ -2938,6 +3029,34 @@ void WebContentsImpl::RenderViewDeleted(RenderViewHost* rvh) { FOR_EACH_OBSERVER(WebContentsObserver, observers_, RenderViewDeleted(rvh)); } +void WebContentsImpl::DidGetResourceResponseStart( + const ResourceRequestDetails& details) { + controller_.ssl_manager()->DidStartResourceResponse(details); + + FOR_EACH_OBSERVER(WebContentsObserver, observers_, + DidGetResourceResponseStart(details)); + + // TODO(avi): Remove. http://crbug.com/170921 + NotificationService::current()->Notify( + NOTIFICATION_RESOURCE_RESPONSE_STARTED, + Source<WebContents>(this), + Details<const ResourceRequestDetails>(&details)); +} + +void WebContentsImpl::DidGetRedirectForResourceRequest( + const ResourceRedirectDetails& details) { + controller_.ssl_manager()->DidReceiveResourceRedirect(details); + + FOR_EACH_OBSERVER(WebContentsObserver, observers_, + DidGetRedirectForResourceRequest(details)); + + // TODO(avi): Remove. http://crbug.com/170921 + NotificationService::current()->Notify( + NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, + Source<WebContents>(this), + Details<const ResourceRedirectDetails>(&details)); +} + void WebContentsImpl::DidNavigate( RenderViewHost* rvh, const ViewHostMsg_FrameNavigate_Params& params) { @@ -3179,10 +3298,8 @@ void WebContentsImpl::DidCancelLoading() { } void WebContentsImpl::DidChangeLoadProgress(double progress) { -#if defined(OS_ANDROID) if (delegate_) delegate_->LoadProgressChanged(this, progress); -#endif } void WebContentsImpl::DidDisownOpener(RenderViewHost* rvh) { @@ -3250,6 +3367,11 @@ void WebContentsImpl::RequestTransferURL( bool user_gesture) { WebContents* new_contents = NULL; PageTransition transition_type = PAGE_TRANSITION_LINK; + GURL dest_url(url); + if (!GetContentClient()->browser()->ShouldAllowOpenURL( + GetSiteInstance(), url)) + dest_url = GURL(kAboutBlankURL); + if (render_manager_.web_ui()) { // When we're a Web UI, it will provide a page transition type for us (this // is so the new tab page can specify AUTO_BOOKMARK for automatically @@ -3259,14 +3381,14 @@ void WebContentsImpl::RequestTransferURL( // want web sites to see a referrer of "chrome://blah" (and some // chrome: URLs might have search terms or other stuff we don't want to // send to the site), so we send no referrer. - OpenURLParams params(url, Referrer(), source_frame_id, disposition, + OpenURLParams params(dest_url, Referrer(), source_frame_id, disposition, render_manager_.web_ui()->GetLinkTransitionType(), false /* is_renderer_initiated */); params.transferred_global_request_id = old_request_id; new_contents = OpenURL(params); transition_type = render_manager_.web_ui()->GetLinkTransitionType(); } else { - OpenURLParams params(url, referrer, source_frame_id, disposition, + OpenURLParams params(dest_url, referrer, source_frame_id, disposition, PAGE_TRANSITION_LINK, true /* is_renderer_initiated */); params.transferred_global_request_id = old_request_id; params.should_replace_current_entry = should_replace_current_entry; @@ -3277,7 +3399,7 @@ void WebContentsImpl::RequestTransferURL( // Notify observers. FOR_EACH_OBSERVER(WebContentsObserver, observers_, DidOpenRequestedURL(new_contents, - url, + dest_url, referrer, disposition, transition_type, @@ -3384,13 +3506,16 @@ void WebContentsImpl::RunJavaScriptMessage( &suppress_this_message); } + *did_suppress_message = suppress_this_message; + if (suppress_this_message) { // If we are suppressing messages, just reply as if the user immediately // pressed "Cancel". OnDialogClosed(rvh, reply_msg, false, string16()); } - *did_suppress_message = suppress_this_message; + // OnDialogClosed (two lines up) may have caused deletion of this object (see + // http://crbug.com/288961 ). The only safe thing to do here is return. } void WebContentsImpl::RunBeforeUnloadConfirm(RenderViewHost* rvh, @@ -3440,7 +3565,8 @@ WebPreferences WebContentsImpl::GetWebkitPrefs() { int WebContentsImpl::CreateSwappedOutRenderView( SiteInstance* instance) { - return render_manager_.CreateRenderView(instance, MSG_ROUTING_NONE, true); + return render_manager_.CreateRenderView(instance, MSG_ROUTING_NONE, + true, true); } void WebContentsImpl::OnUserGesture() { @@ -3458,6 +3584,7 @@ void WebContentsImpl::OnIgnoredUIEvent() { } void WebContentsImpl::RendererUnresponsive(RenderViewHost* rvh, + bool is_during_beforeunload, bool is_during_unload) { // Don't show hung renderer dialog for a swapped out RVH. if (rvh != GetRenderViewHost()) @@ -3471,7 +3598,7 @@ void WebContentsImpl::RendererUnresponsive(RenderViewHost* rvh, if (DevToolsAgentHost::IsDebuggerAttached(this)) return; - if (is_during_unload) { + if (is_during_beforeunload || is_during_unload) { // Hang occurred while firing the beforeunload/unload handler. // Pretend the handler fired so tab closing continues as if it had. rvhi->set_sudden_termination_allowed(true); @@ -3480,10 +3607,17 @@ void WebContentsImpl::RendererUnresponsive(RenderViewHost* rvh, return; // If the tab hangs in the beforeunload/unload handler there's really - // nothing we can do to recover. Pretend the unload listeners have - // all fired and close the tab. If the hang is in the beforeunload handler - // then the user will not have the option of cancelling the close. - Close(rvh); + // nothing we can do to recover. If the hang is in the beforeunload handler, + // pretend the beforeunload listeners have all fired and allow the delegate + // to continue closing; the user will not have the option of cancelling the + // close. Otherwise, pretend the unload listeners have all fired and close + // the tab. + bool close = true; + if (is_during_beforeunload) { + delegate_->BeforeUnloadFired(this, true, &close); + } + if (close) + Close(rvh); return; } @@ -3540,7 +3674,7 @@ void WebContentsImpl::RenderProcessGoneFromRenderManager( void WebContentsImpl::UpdateRenderViewSizeForRenderManager() { // TODO(brettw) this is a hack. See WebContentsView::SizeContents. - gfx::Size size = view_->GetContainerSize(); + gfx::Size size = GetSizeForNewRenderView(); // 0x0 isn't a valid window size (minimal window size is 1x1) but it may be // here during container initialization and normal window size will be set // later. In case of tab duplication this resizing to 0x0 prevents setting @@ -3549,6 +3683,13 @@ void WebContentsImpl::UpdateRenderViewSizeForRenderManager() { view_->SizeContents(size); } +void WebContentsImpl::CancelModalDialogsForRenderManager() { + // We need to cancel modal dialogs when doing a process swap, since the load + // deferrer would prevent us from swapping out. + if (dialog_manager_) + dialog_manager_->CancelActiveAndPendingDialogs(this); +} + void WebContentsImpl::NotifySwappedFromRenderManager(RenderViewHost* rvh) { NotifySwapped(rvh); @@ -3610,7 +3751,8 @@ int WebContentsImpl::CreateOpenerRenderViews(SiteInstance* instance) { // Create a swapped out RenderView in the given SiteInstance if none exists, // setting its opener to the given route_id. Return the new view's route_id. - return render_manager_.CreateRenderView(instance, opener_route_id, true); + return render_manager_.CreateRenderView(instance, opener_route_id, + true, true); } NavigationControllerImpl& WebContentsImpl::GetControllerForRenderManager() { @@ -3634,7 +3776,7 @@ bool WebContentsImpl::CreateRenderViewForRenderManager( // Now that the RenderView has been created, we need to tell it its size. if (rwh_view) - rwh_view->SetSize(view_->GetContainerSize()); + rwh_view->SetSize(GetSizeForNewRenderView()); // Make sure we use the correct starting page_id in the new RenderView. UpdateMaxPageIDIfNecessary(render_view_host); @@ -3648,7 +3790,7 @@ bool WebContentsImpl::CreateRenderViewForRenderManager( return false; } -#if defined(OS_LINUX) || defined(OS_OPENBSD) +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) // Force a ViewMsg_Resize to be sent, needed to make plugins show up on // linux. See crbug.com/83941. if (rwh_view) { @@ -3697,6 +3839,10 @@ void WebContentsImpl::CreateViewAndSetSizeForRVH(RenderViewHost* rvh) { rwh_view->SetSize(GetView()->GetContainerSize()); } +bool WebContentsImpl::IsHidden() { + return capturer_count_ == 0 && !should_normally_be_visible_; +} + RenderViewHostImpl* WebContentsImpl::GetRenderViewHostImpl() { return static_cast<RenderViewHostImpl*>(GetRenderViewHost()); } @@ -3734,4 +3880,13 @@ void WebContentsImpl::ClearAllPowerSaveBlockers() { power_save_blockers_.clear(); } +gfx::Size WebContentsImpl::GetSizeForNewRenderView() const { + gfx::Size size; + if (delegate_) + size = delegate_->GetSizeForNewRenderView(this); + if (size.IsEmpty()) + size = view_->GetContainerSize(); + return size; +} + } // namespace content diff --git a/chromium/content/browser/web_contents/web_contents_impl.h b/chromium/content/browser/web_contents/web_contents_impl.h index a5eaf7ddcbe..c60aebacb30 100644 --- a/chromium/content/browser/web_contents/web_contents_impl.h +++ b/chromium/content/browser/web_contents/web_contents_impl.h @@ -223,6 +223,8 @@ class CONTENT_EXPORT WebContentsImpl virtual int GetEmbeddedInstanceID() const OVERRIDE; virtual int GetRoutingID() const OVERRIDE; virtual RenderWidgetHostView* GetRenderWidgetHostView() const OVERRIDE; + virtual RenderWidgetHostView* GetFullscreenRenderWidgetHostView() const + OVERRIDE; virtual WebContentsView* GetView() const OVERRIDE; virtual WebUI* CreateWebUI(const GURL& url) OVERRIDE; virtual WebUI* GetWebUI() const OVERRIDE; @@ -275,7 +277,7 @@ class CONTENT_EXPORT WebContentsImpl const Referrer& referrer) OVERRIDE; virtual void GenerateMHTML( const base::FilePath& file, - const base::Callback<void(const base::FilePath&, int64)>& callback) + const base::Callback<void(int64)>& callback) OVERRIDE; virtual bool IsActiveEntry(int32 page_id) OVERRIDE; @@ -304,8 +306,7 @@ class CONTENT_EXPORT WebContentsImpl virtual void DidEndColorChooser() OVERRIDE; virtual int DownloadImage(const GURL& url, bool is_favicon, - uint32_t preferred_image_size, - uint32_t max_image_size, + uint32_t max_bitmap_size, const ImageDownloadCallback& callback) OVERRIDE; // Implementation of PageNavigator. @@ -347,6 +348,10 @@ class CONTENT_EXPORT WebContentsImpl RenderViewHost* render_view_host, const ViewHostMsg_DidFailProvisionalLoadWithError_Params& params) OVERRIDE; + virtual void DidGetResourceResponseStart( + const ResourceRequestDetails& details) OVERRIDE; + virtual void DidGetRedirectForResourceRequest( + const ResourceRedirectDetails& details) OVERRIDE; virtual void DidNavigate( RenderViewHost* render_view_host, const ViewHostMsg_FrameNavigate_Params& params) OVERRIDE; @@ -414,6 +419,7 @@ class CONTENT_EXPORT WebContentsImpl virtual void OnUserGesture() OVERRIDE; virtual void OnIgnoredUIEvent() OVERRIDE; virtual void RendererUnresponsive(RenderViewHost* render_view_host, + bool is_during_beforeunload, bool is_during_unload) OVERRIDE; virtual void RendererResponsive(RenderViewHost* render_view_host) OVERRIDE; virtual void LoadStateChanged(const GURL& url, @@ -487,6 +493,7 @@ class CONTENT_EXPORT WebContentsImpl virtual void RenderProcessGoneFromRenderManager( RenderViewHost* render_view_host) OVERRIDE; virtual void UpdateRenderViewSizeForRenderManager() OVERRIDE; + virtual void CancelModalDialogsForRenderManager() OVERRIDE; virtual void NotifySwappedFromRenderManager( RenderViewHost* old_render_view_host) OVERRIDE; virtual int CreateOpenerRenderViewsForRenderManager( @@ -499,6 +506,7 @@ class CONTENT_EXPORT WebContentsImpl virtual bool FocusLocationBarByDefault() OVERRIDE; virtual void SetFocusToLocationBar(bool select_all) OVERRIDE; virtual void CreateViewAndSetSizeForRVH(RenderViewHost* rvh) OVERRIDE; + virtual bool IsHidden() OVERRIDE; // NotificationObserver ------------------------------------------------------ @@ -625,8 +633,8 @@ class CONTENT_EXPORT WebContentsImpl void OnDidDownloadImage(int id, int http_status_code, const GURL& image_url, - int requested_size, - const std::vector<SkBitmap>& bitmaps); + const std::vector<SkBitmap>& bitmaps, + const std::vector<gfx::Size>& original_bitmap_sizes); void OnUpdateFaviconURL(int32 page_id, const std::vector<FaviconURL>& candidates); void OnFrameAttached(int64 parent_frame_id, @@ -738,7 +746,6 @@ class CONTENT_EXPORT WebContentsImpl // Helper functions for sending notifications. void NotifySwapped(RenderViewHost* old_render_view_host); - void NotifyConnected(); void NotifyDisconnected(); void NotifyNavigationEntryCommitted(const LoadCommittedDetails& load_details); @@ -757,6 +764,9 @@ class CONTENT_EXPORT WebContentsImpl // Clear all PowerSaveBlockers, leave power_save_blocker_ empty. void ClearAllPowerSaveBlockers(); + // Helper function to invoke WebContentsDelegate::GetSizeForNewRenderView(). + gfx::Size GetSizeForNewRenderView() const; + // Data for core operation --------------------------------------------------- // Delegate for notifying our owner about stuff. Not owned by us. diff --git a/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc b/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc index 5ea20a6affa..651c2b7d637 100644 --- a/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc +++ b/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc @@ -10,17 +10,38 @@ #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_view.h" #include "content/public/common/content_paths.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" namespace content { +void ResizeWebContentsView(Shell* shell, const gfx::Size& size, + bool set_start_page) { + // Shell::SizeTo is not implemented on Aura; WebContentsView::SizeContents + // works on Win and ChromeOS but not Linux - we need to resize the shell + // window on Linux because if we don't, the next layout of the unchanged shell + // window will resize WebContentsView back to the previous size. + // The cleaner and shorter SizeContents is preferred as more platforms convert + // to Aura. +#if defined(TOOLKIT_GTK) || defined(OS_MACOSX) + shell->SizeTo(size.width(), size.height()); + // If |set_start_page| is true, start with blank page to make sure resize + // takes effect. + if (set_start_page) + NavigateToURL(shell, GURL("about://blank")); +#else + shell->web_contents()->GetView()->SizeContents(size); +#endif // defined(TOOLKIT_GTK) || defined(OS_MACOSX) +} + class WebContentsImplBrowserTest : public ContentBrowserTest { public: WebContentsImplBrowserTest() {} @@ -82,6 +103,51 @@ class NavigateOnCommitObserver : public WebContentsObserver { bool done_; }; +class RenderViewSizeDelegate : public WebContentsDelegate { + public: + void set_size_insets(const gfx::Size& size_insets) { + size_insets_ = size_insets; + } + + // WebContentsDelegate: + virtual gfx::Size GetSizeForNewRenderView( + const WebContents* web_contents) const OVERRIDE { + gfx::Size size(web_contents->GetView()->GetContainerSize()); + size.Enlarge(size_insets_.width(), size_insets_.height()); + return size; + } + + private: + gfx::Size size_insets_; +}; + +class RenderViewSizeObserver : public WebContentsObserver { + public: + RenderViewSizeObserver(Shell* shell, const gfx::Size& wcv_new_size) + : WebContentsObserver(shell->web_contents()), + shell_(shell), + wcv_new_size_(wcv_new_size) { + } + + // WebContentsObserver: + virtual void RenderViewCreated(RenderViewHost* rvh) OVERRIDE { + rwhv_create_size_ = rvh->GetView()->GetViewBounds().size(); + } + + virtual void NavigateToPendingEntry( + const GURL& url, + NavigationController::ReloadType reload_type) OVERRIDE { + ResizeWebContentsView(shell_, wcv_new_size_, false); + } + + gfx::Size rwhv_create_size() const { return rwhv_create_size_; } + + private: + Shell* shell_; // Weak ptr. + gfx::Size wcv_new_size_; + gfx::Size rwhv_create_size_; +}; + // Test that DidStopLoading includes the correct URL in the details. IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, DidStopLoadingDetails) { ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); @@ -186,4 +252,71 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, FrameTree) { EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); } +// TODO(sail): enable this for MAC when auto resizing of WebContentsViewCocoa is +// fixed. +// TODO(shrikant): enable this for Windows when issue with +// force-compositing-mode is resolved (http://crbug.com/281726). +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID) +#define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView +#else +#define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView +#endif +// Test that RenderViewHost is created and updated at the size specified by +// WebContentsDelegate::GetSizeForNewRenderView(). +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, + MAYBE_GetSizeForNewRenderView) { + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + // Create a new server with a different site. + net::SpawnedTestServer https_server( + net::SpawnedTestServer::TYPE_HTTPS, + net::SpawnedTestServer::kLocalhost, + base::FilePath(FILE_PATH_LITERAL("content/test/data"))); + ASSERT_TRUE(https_server.Start()); + + scoped_ptr<RenderViewSizeDelegate> delegate(new RenderViewSizeDelegate()); + shell()->web_contents()->SetDelegate(delegate.get()); + ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get()); + + // When no size is set, RenderWidgetHostView adopts the size of + // WebContenntsView. + NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")); + EXPECT_EQ(shell()->web_contents()->GetView()->GetContainerSize(), + shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds(). + size()); + + // When a size is set, RenderWidgetHostView and WebContentsView honor this + // size. + gfx::Size size(300, 300); + gfx::Size size_insets(-10, -15); + ResizeWebContentsView(shell(), size, true); + delegate->set_size_insets(size_insets); + NavigateToURL(shell(), https_server.GetURL("/")); + size.Enlarge(size_insets.width(), size_insets.height()); + EXPECT_EQ(size, + shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds(). + size()); + EXPECT_EQ(size, shell()->web_contents()->GetView()->GetContainerSize()); + + // If WebContentsView is resized after RenderWidgetHostView is created but + // before pending navigation entry is committed, both RenderWidgetHostView and + // WebContentsView use the new size of WebContentsView. + gfx::Size init_size(200, 200); + gfx::Size new_size(100, 100); + size_insets = gfx::Size(-20, -30); + ResizeWebContentsView(shell(), init_size, true); + delegate->set_size_insets(size_insets); + RenderViewSizeObserver observer(shell(), new_size); + NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); + // RenderWidgetHostView is created at specified size. + init_size.Enlarge(size_insets.width(), size_insets.height()); + EXPECT_EQ(init_size, observer.rwhv_create_size()); + // RenderViewSizeObserver resizes WebContentsView in NavigateToPendingEntry, + // so both WebContentsView and RenderWidgetHostView adopt this new size. + new_size.Enlarge(size_insets.width(), size_insets.height()); + EXPECT_EQ(new_size, + shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds(). + size()); + EXPECT_EQ(new_size, shell()->web_contents()->GetView()->GetContainerSize()); +} + } // namespace content diff --git a/chromium/content/browser/web_contents/web_contents_screenshot_manager.cc b/chromium/content/browser/web_contents/web_contents_screenshot_manager.cc index b2eeb464265..811e4ea7fbc 100644 --- a/chromium/content/browser/web_contents/web_contents_screenshot_manager.cc +++ b/chromium/content/browser/web_contents/web_contents_screenshot_manager.cc @@ -149,7 +149,8 @@ void WebContentsScreenshotManager::OnScreenshotTaken(int unique_id, } if (!success || bitmap.empty() || bitmap.isNull()) { - ClearScreenshot(entry); + if (!ClearScreenshot(entry)) + OnScreenshotSet(entry); return; } @@ -193,7 +194,8 @@ void WebContentsScreenshotManager::OnScreenshotEncodeComplete( } void WebContentsScreenshotManager::OnScreenshotSet(NavigationEntryImpl* entry) { - PurgeScreenshotsIfNecessary(); + if (entry->screenshot().get()) + PurgeScreenshotsIfNecessary(); } bool WebContentsScreenshotManager::ClearScreenshot(NavigationEntryImpl* entry) { diff --git a/chromium/content/browser/web_contents/web_contents_view_android.cc b/chromium/content/browser/web_contents/web_contents_view_android.cc index 1f7b247e606..b51449e2e40 100644 --- a/chromium/content/browser/web_contents/web_contents_view_android.cc +++ b/chromium/content/browser/web_contents/web_contents_view_android.cc @@ -6,13 +6,13 @@ #include "base/logging.h" #include "content/browser/android/content_view_core_impl.h" +#include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" #include "content/browser/renderer_host/render_view_host_factory.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/interstitial_page_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/web_contents_delegate.h" -#include "media/base/android/media_player_manager.h" namespace content { WebContentsViewPort* CreateWebContentsView( diff --git a/chromium/content/browser/web_contents/web_contents_view_aura.cc b/chromium/content/browser/web_contents/web_contents_view_aura.cc index 013edc7fe3e..f3822f8b1c6 100644 --- a/chromium/content/browser/web_contents/web_contents_view_aura.cc +++ b/chromium/content/browser/web_contents/web_contents_view_aura.cc @@ -35,6 +35,7 @@ #include "content/public/browser/web_drag_dest_delegate.h" #include "content/public/common/content_switches.h" #include "content/public/common/drop_data.h" +#include "net/base/net_util.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/drag_drop_client.h" @@ -48,11 +49,11 @@ #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_utils.h" #include "ui/base/dragdrop/os_exchange_data.h" -#include "ui/base/events/event.h" -#include "ui/base/events/event_utils.h" #include "ui/base/hit_test.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_png_rep.h" @@ -235,9 +236,34 @@ class WebDragSourceAura : public base::MessageLoopForUI::Observer, DISALLOW_COPY_AND_ASSIGN(WebDragSourceAura); }; +#if defined(OS_WIN) +// Fill out the OSExchangeData with a file contents, synthesizing a name if +// necessary. +void PrepareDragForFileContents(const DropData& drop_data, + ui::OSExchangeData::Provider* provider) { + base::FilePath file_name(drop_data.file_description_filename); + // Images without ALT text will only have a file extension so we need to + // synthesize one from the provided extension and URL. + if (file_name.BaseName().RemoveExtension().empty()) { + const string16 extension = file_name.Extension(); + // Retrieve the name from the URL. + file_name = base::FilePath(net::GetSuggestedFilename( + drop_data.url, "", "", "", "", "")).ReplaceExtension(extension); + } + provider->SetFileContents(file_name, drop_data.file_contents); +} +#endif + // Utility to fill a ui::OSExchangeDataProvider object from DropData. void PrepareDragData(const DropData& drop_data, ui::OSExchangeData::Provider* provider) { +#if defined(OS_WIN) + // We set the file contents before the URL because the URL also sets file + // contents (to a .URL shortcut). We want to prefer file content data over + // a shortcut so we add it first. + if (!drop_data.file_contents.empty()) + PrepareDragForFileContents(drop_data, provider); +#endif if (!drop_data.text.string().empty()) provider->SetString(drop_data.text.string()); if (drop_data.url.is_valid()) @@ -909,7 +935,7 @@ void WebContentsViewAura::ResetOverscrollTransform() { ui::ScopedLayerAnimationSettings settings(target->layer()->GetAnimator()); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - settings.SetTweenType(ui::Tween::EASE_OUT); + settings.SetTweenType(gfx::Tween::EASE_OUT); settings.AddObserver(this); target->SetTransform(gfx::Transform()); } @@ -917,7 +943,7 @@ void WebContentsViewAura::ResetOverscrollTransform() { ui::ScopedLayerAnimationSettings settings(target->layer()->GetAnimator()); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - settings.SetTweenType(ui::Tween::EASE_OUT); + settings.SetTweenType(gfx::Tween::EASE_OUT); UpdateOverscrollWindowBrightness(0.f); } } @@ -942,7 +968,7 @@ void WebContentsViewAura::CompleteOverscrollNavigation(OverscrollMode mode) { ui::ScopedLayerAnimationSettings settings(target->layer()->GetAnimator()); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - settings.SetTweenType(ui::Tween::EASE_OUT); + settings.SetTweenType(gfx::Tween::EASE_OUT); settings.AddObserver(this); gfx::Transform transform; int content_width = @@ -1169,13 +1195,15 @@ RenderWidgetHostView* WebContentsViewAura::CreateViewForWidget( navigation_overlay_->StartObservingView(ToRenderWidgetHostViewAura(view)); } - view->Show(); + RenderWidgetHostImpl* host_impl = + RenderWidgetHostImpl::From(render_widget_host); + + if (!host_impl->is_hidden()) + view->Show(); // We listen to drag drop events in the newly created view's window. aura::client::SetDragDropDelegate(view->GetNativeView(), this); - RenderWidgetHostImpl* host_impl = - RenderWidgetHostImpl::From(render_widget_host); if (host_impl->overscroll_controller() && (!web_contents_->GetDelegate() || web_contents_->GetDelegate()->CanOverscrollContent())) { @@ -1227,11 +1255,12 @@ void WebContentsViewAura::SetOverscrollControllerEnabled(bool enabled) { // WebContentsViewAura, RenderViewHostDelegateView implementation: void WebContentsViewAura::ShowContextMenu(const ContextMenuParams& params) { - if (delegate_) - delegate_->ShowContextMenu(params); if (touch_editable_) touch_editable_->EndTouchEditing(); - + if (delegate_) { + delegate_->ShowContextMenu(params); + // WARNING: we may have been deleted during the call to ShowContextMenu(). + } } void WebContentsViewAura::ShowPopupMenu(const gfx::Rect& bounds, @@ -1507,11 +1536,8 @@ bool WebContentsViewAura::HasHitTestMask() const { void WebContentsViewAura::GetHitTestMask(gfx::Path* mask) const { } -scoped_refptr<ui::Texture> WebContentsViewAura::CopyTexture() { - // The layer we create doesn't have an external texture, so this should never - // get invoked. - NOTREACHED(); - return scoped_refptr<ui::Texture>(); +void WebContentsViewAura::DidRecreateLayer(ui::Layer *old_layer, + ui::Layer *new_layer) { } //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/content/browser/web_contents/web_contents_view_aura.h b/chromium/content/browser/web_contents/web_contents_view_aura.h index ca1f99c0a06..aca720e0763 100644 --- a/chromium/content/browser/web_contents/web_contents_view_aura.h +++ b/chromium/content/browser/web_contents/web_contents_view_aura.h @@ -172,7 +172,8 @@ class CONTENT_EXPORT WebContentsViewAura virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; virtual bool HasHitTestMask() const OVERRIDE; virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; - virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE; + virtual void DidRecreateLayer(ui::Layer* old_layer, + ui::Layer* new_layer) OVERRIDE; // Overridden from ui::EventHandler: virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; diff --git a/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc index e0f34abe4fc..a48cde5b170 100644 --- a/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc +++ b/chromium/content/browser/web_contents/web_contents_view_aura_browsertest.cc @@ -9,16 +9,20 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/test_timeouts.h" #include "base/values.h" +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/navigation_controller_impl.h" #include "content/browser/web_contents/navigation_entry_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_screenshot_manager.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_view.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "ui/aura/root_window.h" @@ -44,6 +48,7 @@ class ScreenshotTracker : public WebContentsScreenshotManager { void Reset() { screenshot_taken_for_ = NULL; + screenshot_set_.clear(); } void SetScreenshotInterval(int interval_ms) { @@ -57,6 +62,10 @@ class ScreenshotTracker : public WebContentsScreenshotManager { message_loop_runner_->Run(); } + bool ScreenshotSetForEntry(NavigationEntryImpl* entry) const { + return screenshot_set_.count(entry) > 0; + } + private: // Overridden from WebContentsScreenshotManager: virtual void TakeScreenshotImpl(RenderViewHost* host, @@ -68,6 +77,7 @@ class ScreenshotTracker : public WebContentsScreenshotManager { virtual void OnScreenshotSet(NavigationEntryImpl* entry) OVERRIDE { --waiting_for_screenshots_; + screenshot_set_[entry] = true; WebContentsScreenshotManager::OnScreenshotSet(entry); if (waiting_for_screenshots_ == 0 && message_loop_runner_.get()) message_loop_runner_->Quit(); @@ -76,10 +86,42 @@ class ScreenshotTracker : public WebContentsScreenshotManager { RenderViewHost* screenshot_taken_for_; scoped_refptr<content::MessageLoopRunner> message_loop_runner_; int waiting_for_screenshots_; + std::map<NavigationEntryImpl*, bool> screenshot_set_; DISALLOW_COPY_AND_ASSIGN(ScreenshotTracker); }; +class NavigationWatcher : public WebContentsObserver { + public: + explicit NavigationWatcher(WebContents* contents) + : WebContentsObserver(contents), + navigated_(false), + should_quit_loop_(false) { + } + + virtual ~NavigationWatcher() {} + + void WaitUntilNavigationStarts() { + if (navigated_) + return; + should_quit_loop_ = true; + base::MessageLoop::current()->Run(); + } + + private: + // Overridden from WebContentsObserver: + virtual void AboutToNavigateRenderView(RenderViewHost* host) OVERRIDE { + navigated_ = true; + if (should_quit_loop_) + base::MessageLoop::current()->Quit(); + } + + bool navigated_; + bool should_quit_loop_; + + DISALLOW_COPY_AND_ASSIGN(NavigationWatcher); +}; + class WebContentsViewAuraTest : public ContentBrowserTest { public: WebContentsViewAuraTest() @@ -89,6 +131,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest { virtual void SetUp() OVERRIDE { // TODO(jbauman): Remove this. http://crbug.com/268644 UseRealGLContexts(); + ContentBrowserTest::SetUp(); } // Executes the javascript synchronously and makes sure the returned value is @@ -151,6 +194,8 @@ class WebContentsViewAuraTest : public ContentBrowserTest { aura::Window* content = web_contents->GetView()->GetContentNativeView(); gfx::Rect bounds = content->GetBoundsInRootWindow(); aura::test::EventGenerator generator(content->GetRootWindow(), content); + const int kScrollDurationMs = 20; + const int kScrollSteps = 10; { // Do a swipe-right now. That should navigate backwards. @@ -159,8 +204,8 @@ class WebContentsViewAuraTest : public ContentBrowserTest { generator.GestureScrollSequence( gfx::Point(bounds.x() + 2, bounds.y() + 10), gfx::Point(bounds.right() - 10, bounds.y() + 10), - base::TimeDelta::FromMilliseconds(20), - 1); + base::TimeDelta::FromMilliseconds(kScrollDurationMs), + kScrollSteps); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); value = content::ExecuteScriptAndGetValue(view_host, "get_current()"); @@ -177,8 +222,8 @@ class WebContentsViewAuraTest : public ContentBrowserTest { generator.GestureScrollSequence( gfx::Point(bounds.x() + 2, bounds.y() + 10), gfx::Point(bounds.right() - 10, bounds.y() + 10), - base::TimeDelta::FromMilliseconds(20), - 10); + base::TimeDelta::FromMilliseconds(kScrollDurationMs), + kScrollSteps); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); value = content::ExecuteScriptAndGetValue(view_host, "get_current()"); @@ -195,8 +240,8 @@ class WebContentsViewAuraTest : public ContentBrowserTest { generator.GestureScrollSequence( gfx::Point(bounds.right() - 10, bounds.y() + 10), gfx::Point(bounds.x() + 2, bounds.y() + 10), - base::TimeDelta::FromMilliseconds(20), - 10); + base::TimeDelta::FromMilliseconds(kScrollDurationMs), + kScrollSteps); string16 actual_title = title_watcher.WaitAndGetTitle(); EXPECT_EQ(expected_title, actual_title); value = content::ExecuteScriptAndGetValue(view_host, "get_current()"); @@ -328,8 +373,15 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, // - interactively, when user does an overscroll gesture // - interactively, when user navigates in history without the overscroll // gesture. -IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, - OverscrollScreenshot) { +IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OverscrollScreenshot) { + // Disable the test for WinXP. See http://crbug/294116. +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + LOG(WARNING) << "Test disabled due to unknown bug on WinXP."; + return; + } +#endif + ASSERT_NO_FATAL_FAILURE( StartTestWithPage("files/overscroll_navigation.html")); WebContentsImpl* web_contents = @@ -354,11 +406,11 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtIndex(1)); - EXPECT_TRUE(entry->screenshot().get()); + EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry)); entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtIndex(0)); - EXPECT_TRUE(entry->screenshot().get()); + EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry)); // Navigate again. Index 2 should now have a screenshot. ExecuteSyncJSFunction(view_host, "navigate_next()"); @@ -367,7 +419,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtIndex(2)); - EXPECT_TRUE(entry->screenshot().get()); + EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry)); entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtIndex(3)); @@ -392,7 +444,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, screenshot_manager()->WaitUntilScreenshotIsReady(); entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtIndex(3)); - EXPECT_TRUE(entry->screenshot().get()); + EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry)); } // Navigate a couple more times. @@ -416,7 +468,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, screenshot_manager()->WaitUntilScreenshotIsReady(); entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtIndex(4)); - EXPECT_TRUE(entry->screenshot().get()); + EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry)); } } @@ -464,15 +516,16 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, EXPECT_NE(old_host, web_contents->GetRenderViewHost()) << navigations[i].url.spec(); EXPECT_EQ(old_host, screenshot_manager()->screenshot_taken_for()); - screenshot_manager()->Reset(); NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetEntryAtOffset(-1)); - EXPECT_TRUE(entry->screenshot().get()); + EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry)); entry = NavigationEntryImpl::FromNavigationEntry( web_contents->GetController().GetActiveEntry()); + EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry)); EXPECT_FALSE(entry->screenshot().get()); + screenshot_manager()->Reset(); } // Increase the minimum interval between taking screenshots. @@ -568,6 +621,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, web_contents->GetController().GoBack(); WaitForLoadStop(web_contents); EXPECT_EQ(1, GetCurrentIndex()); + EXPECT_EQ(base::ASCIIToUTF16("Title: #1"), web_contents->GetTitle()); EXPECT_TRUE(controller.CanGoBack()); EXPECT_TRUE(controller.CanGoForward()); @@ -579,18 +633,15 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, // right. string16 expected_title = ASCIIToUTF16("Title: #2"); content::TitleWatcher title_watcher(web_contents, expected_title); + NavigationWatcher nav_watcher(web_contents); generator.GestureScrollSequence( gfx::Point(bounds.right() - 10, bounds.y() + 10), gfx::Point(bounds.x() + 2, bounds.y() + 10), base::TimeDelta::FromMilliseconds(2000), 10); - // Make sure the GestureEventFilter's debouncing doesn't interfere. - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::MessageLoop::QuitClosure(), - base::TimeDelta::FromMilliseconds(100)); - base::MessageLoop::current()->Run(); + nav_watcher.WaitUntilNavigationStarts(); + generator.GestureScrollSequence( gfx::Point(bounds.x() + 2, bounds.y() + 10), gfx::Point(bounds.right() - 10, bounds.y() + 10), diff --git a/chromium/content/browser/web_contents/web_contents_view_mac.mm b/chromium/content/browser/web_contents/web_contents_view_mac.mm index 6a9459d3e95..d1ab628b33e 100644 --- a/chromium/content/browser/web_contents/web_contents_view_mac.mm +++ b/chromium/content/browser/web_contents/web_contents_view_mac.mm @@ -512,6 +512,18 @@ void WebContentsViewMac::CloseTab() { [dragSource_ moveDragTo:screenPoint]; } +// Called when a file drag is dropped and the promised files need to be written. +- (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDest { + if (![dropDest isFileURL]) + return nil; + + NSString* fileName = [dragSource_ dragPromisedFileTo:[dropDest path]]; + if (!fileName) + return nil; + + return @[ fileName ]; +} + // NSDraggingDestination methods - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender { diff --git a/chromium/content/browser/web_contents/web_contents_view_win.cc b/chromium/content/browser/web_contents/web_contents_view_win.cc index 6a75e71cab5..0c65b1dc181 100644 --- a/chromium/content/browser/web_contents/web_contents_view_win.cc +++ b/chromium/content/browser/web_contents/web_contents_view_win.cc @@ -246,6 +246,7 @@ void WebContentsViewWin::SetOverscrollControllerEnabled(bool enabled) { void WebContentsViewWin::ShowContextMenu(const ContextMenuParams& params) { if (delegate_) delegate_->ShowContextMenu(params); + // WARNING: this may have been deleted. } void WebContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds, diff --git a/chromium/content/browser/web_contents/web_contents_view_win.h b/chromium/content/browser/web_contents/web_contents_view_win.h index 3ca6b48765a..adfe6751fad 100644 --- a/chromium/content/browser/web_contents/web_contents_view_win.h +++ b/chromium/content/browser/web_contents/web_contents_view_win.h @@ -13,7 +13,7 @@ #include "content/common/drag_event_source_info.h" #include "content/port/browser/render_view_host_delegate_view.h" #include "content/port/browser/web_contents_view_port.h" -#include "ui/base/win/window_impl.h" +#include "ui/gfx/win/window_impl.h" namespace ui { class HWNDMessageFilter; @@ -29,7 +29,7 @@ class WebDragDest; class CONTENT_EXPORT WebContentsViewWin : public WebContentsViewPort, public RenderViewHostDelegateView, - public ui::WindowImpl { + public gfx::WindowImpl { public: WebContentsViewWin(WebContentsImpl* web_contents, WebContentsViewDelegate* delegate); diff --git a/chromium/content/browser/web_contents/web_drag_source_gtk.cc b/chromium/content/browser/web_contents/web_drag_source_gtk.cc index 0f1d9d57876..0ad27de95c8 100644 --- a/chromium/content/browser/web_contents/web_drag_source_gtk.cc +++ b/chromium/content/browser/web_contents/web_drag_source_gtk.cc @@ -24,8 +24,8 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/dragdrop/gtk_dnd_util.h" -#include "ui/base/gtk/gtk_compat.h" #include "ui/base/gtk/gtk_screen_util.h" +#include "ui/gfx/gtk_compat.h" #include "ui/gfx/gtk_util.h" using WebKit::WebDragOperation; diff --git a/chromium/content/browser/web_contents/web_drag_source_mac.h b/chromium/content/browser/web_contents/web_drag_source_mac.h index 4b18953abf6..f355728a594 100644 --- a/chromium/content/browser/web_contents/web_drag_source_mac.h +++ b/chromium/content/browser/web_contents/web_drag_source_mac.h @@ -86,4 +86,10 @@ CONTENT_EXPORT // Drag moved; hook up to -draggedImage:movedTo:. - (void)moveDragTo:(NSPoint)screenPoint; +// Call to drag a promised file to the given path (should be called before +// -endDragAt:...); hook up to -namesOfPromisedFilesDroppedAtDestination:. +// Returns the file name (not including path) of the file deposited (or which +// will be deposited). +- (NSString*)dragPromisedFileTo:(NSString*)path; + @end diff --git a/chromium/content/browser/web_contents/web_drag_source_mac.mm b/chromium/content/browser/web_contents/web_drag_source_mac.mm index 0b44fd47930..041737935e9 100644 --- a/chromium/content/browser/web_contents/web_drag_source_mac.mm +++ b/chromium/content/browser/web_contents/web_drag_source_mac.mm @@ -8,9 +8,7 @@ #include "base/bind.h" #include "base/files/file_path.h" -#include "base/mac/mac_logging.h" #include "base/mac/mac_util.h" -#include "base/mac/scoped_aedesc.h" #include "base/pickle.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" @@ -93,121 +91,11 @@ void PromiseWriterHelper(const DropData& drop_data, drop_data.file_contents.length()); } -// Returns the drop location from a pasteboard. -NSString* GetDropLocation(NSPasteboard* pboard) { - // The API to get the drop location during a callback from - // kPasteboardTypeFileURLPromise is PasteboardCopyPasteLocation, which takes - // a PasteboardRef, which isn't bridged with NSPasteboard. Ugh. - - PasteboardRef pb_ref = NULL; - OSStatus status = PasteboardCreate(base::mac::NSToCFCast([pboard name]), - &pb_ref); - if (status != noErr || !pb_ref) { - OSSTATUS_DCHECK(false, status) << "Error finding Carbon pasteboard"; - return nil; - } - base::ScopedCFTypeRef<PasteboardRef> pb_ref_scoper(pb_ref); - PasteboardSynchronize(pb_ref); - - CFURLRef drop_url = NULL; - status = PasteboardCopyPasteLocation(pb_ref, &drop_url); - if (status != noErr || !drop_url) { - OSSTATUS_DCHECK(false, status) << "Error finding drop location"; - return nil; - } - base::ScopedCFTypeRef<CFURLRef> drop_url_scoper(drop_url); - - NSString* drop_path = [base::mac::CFToNSCast(drop_url) path]; - return drop_path; -} - -void SelectFileInFinder(const base::FilePath& file_path) { - DCHECK([NSThread isMainThread]); - - // Create the target of this AppleEvent, the Finder. - base::mac::ScopedAEDesc<AEAddressDesc> address; - const OSType finder_creator_code = 'MACS'; - OSErr status = AECreateDesc(typeApplSignature, // type - &finder_creator_code, // data - sizeof(finder_creator_code), // dataSize - address.OutPointer()); // result - if (status != noErr) { - OSSTATUS_LOG(WARNING, status) << "Could not create SelectFile() AE target"; - return; - } - - // Build the AppleEvent data structure that instructs Finder to select files. - base::mac::ScopedAEDesc<AppleEvent> the_event; - status = AECreateAppleEvent(kAEMiscStandards, // theAEEventClass - kAESelect, // theAEEventID - address, // target - kAutoGenerateReturnID, // returnID - kAnyTransactionID, // transactionID - the_event.OutPointer()); // result - if (status != noErr) { - OSSTATUS_LOG(WARNING, status) << "Could not create SelectFile() AE event"; - return; - } - - // Create the list of files (only ever one) to select. - base::mac::ScopedAEDesc<AEDescList> file_list; - status = AECreateList(NULL, // factoringPtr - 0, // factoredSize - false, // isRecord - file_list.OutPointer()); // resultList - if (status != noErr) { - OSSTATUS_LOG(WARNING, status) - << "Could not create SelectFile() AE file list"; - return; - } - - // Add the single path to the file list. - NSURL* url = [NSURL fileURLWithPath:SysUTF8ToNSString(file_path.value())]; - base::ScopedCFTypeRef<CFDataRef> url_data( - CFURLCreateData(kCFAllocatorDefault, base::mac::NSToCFCast(url), - kCFStringEncodingUTF8, true)); - status = AEPutPtr(file_list.OutPointer(), // theAEDescList - 0, // index - typeFileURL, // typeCode - CFDataGetBytePtr(url_data), // dataPtr - CFDataGetLength(url_data)); // dataSize - if (status != noErr) { - OSSTATUS_LOG(WARNING, status) - << "Could not add file path to AE list in SelectFile()"; - return; - } - - // Attach the file list to the AppleEvent. - status = AEPutParamDesc(the_event.OutPointer(), // theAppleEvent - keyDirectObject, // theAEKeyword - file_list); // theAEDesc - if (status != noErr) { - OSSTATUS_LOG(WARNING, status) - << "Could not put the AE file list the path in SelectFile()"; - return; - } - - // Send the actual event. Do not care about the reply. - base::mac::ScopedAEDesc<AppleEvent> reply; - status = AESend(the_event, // theAppleEvent - reply.OutPointer(), // reply - kAENoReply + kAEAlwaysInteract, // sendMode - kAENormalPriority, // sendPriority - kAEDefaultTimeout, // timeOutInTicks - NULL, // idleProc - NULL); // filterProc - if (status != noErr) { - OSSTATUS_LOG(WARNING, status) - << "Could not send AE to Finder in SelectFile()"; - } -} - } // namespace @interface WebDragSource(Private) -- (void)writePromisedFileTo:(NSString*)path; - (void)fillPasteboard; - (NSImage*)dragImage; @@ -282,7 +170,7 @@ void SelectFileInFinder(const base::FilePath& file_path) { NSURL* url = [NSURL URLWithString:SysUTF8ToNSString(dropData_->url.spec())]; // If NSURL creation failed, check for a badly-escaped JavaScript URL. // Strip out any existing escapes and then re-escape uniformly. - if (!url && dropData_->url.SchemeIs(chrome::kJavaScriptScheme)) { + if (!url && dropData_->url.SchemeIs(content::kJavaScriptScheme)) { net::UnescapeRule::Type unescapeRules = net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS | @@ -324,18 +212,6 @@ void SelectFileInFinder(const base::FilePath& file_path) { [pboard setData:[NSData data] forType:ui::kChromeDragDummyPboardType]; - // File promise. - } else if ([type isEqualToString: - base::mac::CFToNSCast(kPasteboardTypeFileURLPromise)]) { - NSString* destination = GetDropLocation(pboard); - if (destination) { - [self writePromisedFileTo:destination]; - - // And set some data. - [pboard setData:[NSData data] - forType:base::mac::CFToNSCast(kPasteboardTypeFileURLPromise)]; - } - // Oops! } else { // Unknown drag pasteboard type. @@ -437,15 +313,11 @@ void SelectFileInFinder(const base::FilePath& file_path) { } } -@end // @implementation WebDragSource - -@implementation WebDragSource (Private) - -- (void)writePromisedFileTo:(NSString*)path { +- (NSString*)dragPromisedFileTo:(NSString*)path { // Be extra paranoid; avoid crashing. if (!dropData_) { NOTREACHED() << "No drag-and-drop data available for promised file."; - return; + return nil; } base::FilePath filePath(SysNSStringToUTF8(path)); @@ -458,14 +330,15 @@ void SelectFileInFinder(const base::FilePath& file_path) { scoped_ptr<FileStream> fileStream(content::CreateFileStreamForDrop( &filePath, content::GetContentClient()->browser()->GetNetLog())); if (!fileStream) - return; + return nil; if (downloadURL_.is_valid()) { scoped_refptr<DragDownloadFile> dragFileDownloader(new DragDownloadFile( filePath, fileStream.Pass(), downloadURL_, - content::Referrer(contents_->GetURL(), dropData_->referrer_policy), + content::Referrer(contents_->GetLastCommittedURL(), + dropData_->referrer_policy), contents_->GetEncoding(), contents_)); @@ -480,18 +353,26 @@ void SelectFileInFinder(const base::FilePath& file_path) { *dropData_, base::Passed(&fileStream))); } - SelectFileInFinder(filePath); + + // The DragDownloadFile constructor may have altered the value of |filePath| + // if, say, an existing file at the drop site has the same name. Return the + // actual name that was used to write the file. + return SysUTF8ToNSString(filePath.BaseName().value()); } +@end // @implementation WebDragSource + +@implementation WebDragSource (Private) + - (void)fillPasteboard { DCHECK(pasteboard_.get()); - [pasteboard_ declareTypes:@[ui::kChromeDragDummyPboardType] + [pasteboard_ declareTypes:@[ ui::kChromeDragDummyPboardType ] owner:contentsView_]; // URL (and title). if (dropData_->url.is_valid()) { - [pasteboard_ addTypes:@[NSURLPboardType, kNSURLTitlePboardType] + [pasteboard_ addTypes:@[ NSURLPboardType, kNSURLTitlePboardType ] owner:contentsView_]; } @@ -533,18 +414,33 @@ void SelectFileInFinder(const base::FilePath& file_path) { fileUTI_.reset(UTTypeCreatePreferredIdentifierForTag( kUTTagClassMIMEType, mimeTypeCF.get(), NULL)); - NSArray* types = - @[base::mac::CFToNSCast(kPasteboardTypeFileURLPromise), - base::mac::CFToNSCast(kPasteboardTypeFilePromiseContent)]; - [pasteboard_ addTypes:types owner:contentsView_]; - - [pasteboard_ setPropertyList:@[base::mac::CFToNSCast(fileUTI_.get())] - forType:base::mac::CFToNSCast(kPasteboardTypeFilePromiseContent)]; - - if (!dropData_->file_contents.empty()) { - NSArray* types = @[base::mac::CFToNSCast(fileUTI_.get())]; - [pasteboard_ addTypes:types owner:contentsView_]; - } + // File (HFS) promise. + // There are two ways to drag/drop files. NSFilesPromisePboardType is the + // deprecated way, and kPasteboardTypeFilePromiseContent is the way that + // does not work. kPasteboardTypeFilePromiseContent is thoroughly broken: + // * API: There is no good way to get the location for the drop. + // <http://lists.apple.com/archives/cocoa-dev/2012/Feb/msg00706.html> + // <rdar://14943849> <http://openradar.me/14943849> + // * Behavior: A file dropped in the Finder is not selected. This can be + // worked around by selecting the file in the Finder using AppleEvents, + // but the drop target window will come to the front of the Finder's + // window list (unlike the previous behavior). <http://crbug.com/278515> + // <rdar://14943865> <http://openradar.me/14943865> + // * Behavior: Files dragged over app icons in the dock do not highlight + // the dock icons, and the dock icons do not accept the drop. + // <http://crbug.com/282916> <rdar://14943872> + // <http://openradar.me/14943872> + // * Behavior: A file dropped onto the desktop is positioned at the upper + // right of the desktop rather than at the position at which it was + // dropped. <http://crbug.com/284942> <rdar://14943881> + // <http://openradar.me/14943881> + NSArray* fileUTIList = @[ base::mac::CFToNSCast(fileUTI_.get()) ]; + [pasteboard_ addTypes:@[ NSFilesPromisePboardType ] owner:contentsView_]; + [pasteboard_ setPropertyList:fileUTIList + forType:NSFilesPromisePboardType]; + + if (!dropData_->file_contents.empty()) + [pasteboard_ addTypes:fileUTIList owner:contentsView_]; } } @@ -562,21 +458,21 @@ void SelectFileInFinder(const base::FilePath& file_path) { UTTypeConformsTo(fileUTI_.get(), kUTTypeImage); if (hasHTMLData) { if (hasImageData) { - [pasteboard_ addTypes:@[ui::kChromeDragImageHTMLPboardType] + [pasteboard_ addTypes:@[ ui::kChromeDragImageHTMLPboardType ] owner:contentsView_]; } else { - [pasteboard_ addTypes:@[NSHTMLPboardType] owner:contentsView_]; + [pasteboard_ addTypes:@[ NSHTMLPboardType ] owner:contentsView_]; } } // Plain text. if (!dropData_->text.string().empty()) { - [pasteboard_ addTypes:@[NSStringPboardType] + [pasteboard_ addTypes:@[ NSStringPboardType ] owner:contentsView_]; } if (!dropData_->custom_data.empty()) { - [pasteboard_ addTypes:@[ui::kWebCustomDataPboardType] + [pasteboard_ addTypes:@[ ui::kWebCustomDataPboardType ] owner:contentsView_]; } } diff --git a/chromium/content/browser/webkit_browsertest.cc b/chromium/content/browser/webkit_browsertest.cc index 24c24a60caa..5394f5bcc55 100644 --- a/chromium/content/browser/webkit_browsertest.cc +++ b/chromium/content/browser/webkit_browsertest.cc @@ -4,7 +4,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/web_contents.h" -#include "content/shell/shell.h" +#include "content/shell/browser/shell.h" #include "content/test/content_browser_test.h" #include "content/test/content_browser_test_utils.h" #include "content/test/net/url_request_abort_on_end_job.h" diff --git a/chromium/content/browser/webui/shared_resources_data_source.cc b/chromium/content/browser/webui/shared_resources_data_source.cc index 23cdc3d23a3..45ddc793a65 100644 --- a/chromium/content/browser/webui/shared_resources_data_source.cc +++ b/chromium/content/browser/webui/shared_resources_data_source.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/memory/ref_counted_memory.h" +#include "base/strings/string_util.h" #include "base/threading/thread_restrictions.h" #include "content/public/common/content_client.h" #include "content/public/common/url_constants.h" @@ -14,10 +15,37 @@ namespace { +const char kAppImagesPath[] = "images/apps/"; +const char kAppImagesPath2x[] = "images/2x/apps/"; + +const char kReplacement[] = "../../resources/default_100_percent/common/"; +const char kReplacement2x[] = "../../resources/default_200_percent/common/"; + +// This entire method is a hack introduced to be able to handle apps images +// that exist in the ui/resources directory. From JS/CSS, we still load the +// image as if it were chrome://resources/images/apps/myappimage.png, if that +// path doesn't exist, we check to see if it that image exists in the relative +// path to ui/resources instead. +// TODO(rkc): Once we have a separate source for apps, remove this code. +bool AppsRelativePathMatch(const std::string& path, + const std::string& compareto) { + if (StartsWithASCII(path, kAppImagesPath, false)) { + if (compareto == + (kReplacement + path.substr(arraysize(kAppImagesPath) - 1))) + return true; + } else if (StartsWithASCII(path, kAppImagesPath2x, false)) { + if (compareto == + (kReplacement2x + path.substr(arraysize(kAppImagesPath2x) - 1))) + return true; + } + return false; +} + int PathToIDR(const std::string& path) { int idr = -1; for (size_t i = 0; i < kWebuiResourcesSize; ++i) { - if (kWebuiResources[i].name == path) { + if ((path == kWebuiResources[i].name) || + AppsRelativePathMatch(path, kWebuiResources[i].name)) { idr = kWebuiResources[i].value; break; } diff --git a/chromium/content/browser/webui/url_data_manager_backend.cc b/chromium/content/browser/webui/url_data_manager_backend.cc index 7c4c77b9bb9..1bcbdb76e9e 100644 --- a/chromium/content/browser/webui/url_data_manager_backend.cc +++ b/chromium/content/browser/webui/url_data_manager_backend.cc @@ -388,7 +388,7 @@ class ChromeProtocolHandler // Next check for chrome://blob-internals/, which uses its own job type. if (ViewBlobInternalsJobFactory::IsSupportedURL(request->url())) { return ViewBlobInternalsJobFactory::CreateJobForRequest( - request, network_delegate, blob_storage_context_->controller()); + request, network_delegate, blob_storage_context_->context()); } #if defined(USE_TCMALLOC) diff --git a/chromium/content/browser/webui/web_ui_controller_factory_registry.cc b/chromium/content/browser/webui/web_ui_controller_factory_registry.cc index 675bd8f8d0b..ede404d9939 100644 --- a/chromium/content/browser/webui/web_ui_controller_factory_registry.cc +++ b/chromium/content/browser/webui/web_ui_controller_factory_registry.cc @@ -82,7 +82,7 @@ bool WebUIControllerFactoryRegistry::IsURLAcceptableForWebUI( bool data_urls_allowed) const { return UseWebUIForURL(browser_context, url) || // javascript: URLs are allowed to run in Web UI pages. - url.SchemeIs(chrome::kJavaScriptScheme) || + url.SchemeIs(kJavaScriptScheme) || // It's possible to load about:blank in a Web UI renderer. // See http://crbug.com/42547 url.spec() == kAboutBlankURL || diff --git a/chromium/content/browser/webui/web_ui_data_source_impl.cc b/chromium/content/browser/webui/web_ui_data_source_impl.cc index b564e57599b..9d0d17128a6 100644 --- a/chromium/content/browser/webui/web_ui_data_source_impl.cc +++ b/chromium/content/browser/webui/web_ui_data_source_impl.cc @@ -49,6 +49,9 @@ class WebUIDataSourceImpl::InternalDataSource : public URLDataSource { return parent_->StartDataRequest(path, render_process_id, render_view_id, callback); } + virtual bool ShouldReplaceExistingSource() const OVERRIDE { + return parent_->replace_existing_source_; + } virtual bool ShouldAddContentSecurityPolicy() const OVERRIDE { return parent_->add_csp_; } @@ -81,7 +84,8 @@ WebUIDataSourceImpl::WebUIDataSourceImpl(const std::string& source_name) object_src_set_(false), frame_src_set_(false), deny_xframe_options_(true), - disable_set_font_strings_(false) { + disable_set_font_strings_(false), + replace_existing_source_(true) { } WebUIDataSourceImpl::~WebUIDataSourceImpl() { @@ -134,6 +138,10 @@ void WebUIDataSourceImpl::SetRequestFilter( filter_callback_ = callback; } +void WebUIDataSourceImpl::DisableReplaceExistingSource() { + replace_existing_source_ = false; +} + void WebUIDataSourceImpl::DisableContentSecurityPolicy() { add_csp_ = false; } @@ -168,6 +176,9 @@ std::string WebUIDataSourceImpl::GetMimeType(const std::string& path) const { if (EndsWith(path, ".pdf", false)) return "application/pdf"; + if (EndsWith(path, ".svg", false)) + return "image/svg+xml"; + return "text/html"; } diff --git a/chromium/content/browser/webui/web_ui_data_source_impl.h b/chromium/content/browser/webui/web_ui_data_source_impl.h index 0b6f8a355d3..1f8100469e7 100644 --- a/chromium/content/browser/webui/web_ui_data_source_impl.h +++ b/chromium/content/browser/webui/web_ui_data_source_impl.h @@ -42,6 +42,7 @@ class CONTENT_EXPORT WebUIDataSourceImpl virtual void SetDefaultResource(int resource_id) OVERRIDE; virtual void SetRequestFilter( const WebUIDataSource::HandleRequestCallback& callback) OVERRIDE; + virtual void DisableReplaceExistingSource() OVERRIDE; virtual void DisableContentSecurityPolicy() OVERRIDE; virtual void OverrideContentSecurityPolicyObjectSrc( const std::string& data) OVERRIDE; @@ -99,6 +100,7 @@ class CONTENT_EXPORT WebUIDataSourceImpl std::string frame_src_; bool deny_xframe_options_; bool disable_set_font_strings_; + bool replace_existing_source_; DISALLOW_COPY_AND_ASSIGN(WebUIDataSourceImpl); }; diff --git a/chromium/content/browser/worker_host/message_port_service.cc b/chromium/content/browser/worker_host/message_port_service.cc index d0563f0c23a..b413c1f8c60 100644 --- a/chromium/content/browser/worker_host/message_port_service.cc +++ b/chromium/content/browser/worker_host/message_port_service.cc @@ -124,10 +124,8 @@ void MessagePortService::PostMessageTo( MessagePort& entangled_port = message_ports_[message_port_id]; std::vector<MessagePort*> sent_ports(sent_message_port_ids.size()); - for (size_t i = 0; i < sent_message_port_ids.size(); ++i) { + for (size_t i = 0; i < sent_message_port_ids.size(); ++i) sent_ports[i] = &message_ports_[sent_message_port_ids[i]]; - sent_ports[i]->queue_messages = true; - } if (entangled_port.queue_messages) { entangled_port.queued_messages.push_back( diff --git a/chromium/content/browser/worker_host/message_port_service.h b/chromium/content/browser/worker_host/message_port_service.h index e6bf71767bb..30645c79b65 100644 --- a/chromium/content/browser/worker_host/message_port_service.h +++ b/chromium/content/browser/worker_host/message_port_service.h @@ -72,6 +72,13 @@ class MessagePortService { // The globally unique id of the entangled message port. int entangled_message_port_id; // If true, all messages to this message port are queued and not delivered. + // This is needed so that when a message port is sent between processes all + // pending message get transferred. There are two possibilities for pending + // messages: either they are already received by the child process, or they're + // in-flight. This flag ensures that the latter type get flushed through the + // system. + // This flag should only be set to true in response to + // WorkerProcessHostMsg_QueueMessages. bool queue_messages; QueuedMessages queued_messages; }; diff --git a/chromium/content/browser/worker_host/worker_process_host.cc b/chromium/content/browser/worker_host/worker_process_host.cc index fa37574a772..a228f9cf0eb 100644 --- a/chromium/content/browser/worker_host/worker_process_host.cc +++ b/chromium/content/browser/worker_host/worker_process_host.cc @@ -24,6 +24,7 @@ #include "content/browser/devtools/worker_devtools_message_filter.h" #include "content/browser/fileapi/fileapi_message_filter.h" #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" +#include "content/browser/loader/resource_message_filter.h" #include "content/browser/mime_registry_message_filter.h" #include "content/browser/quota_dispatcher_host.h" #include "content/browser/renderer_host/database_message_filter.h" @@ -75,31 +76,6 @@ class WorkerSandboxedProcessLauncherDelegate }; #endif // OS_WIN -// Helper class that we pass to SocketStreamDispatcherHost so that it can find -// the right net::URLRequestContext for a request. -class URLRequestContextSelector - : public ResourceMessageFilter::URLRequestContextSelector { - public: - explicit URLRequestContextSelector( - net::URLRequestContextGetter* url_request_context, - net::URLRequestContextGetter* media_url_request_context) - : url_request_context_(url_request_context), - media_url_request_context_(media_url_request_context) { - } - virtual ~URLRequestContextSelector() {} - - virtual net::URLRequestContext* GetRequestContext( - ResourceType::Type resource_type) OVERRIDE { - if (resource_type == ResourceType::MEDIA) - return media_url_request_context_->GetURLRequestContext(); - return url_request_context_->GetURLRequestContext(); - } - - private: - net::URLRequestContextGetter* url_request_context_; - net::URLRequestContextGetter* media_url_request_context_; -}; - } // namespace // Notifies RenderViewHost that one or more worker objects crashed. @@ -215,7 +191,7 @@ bool WorkerProcessHost::Init(int render_process_id) { new WorkerSandboxedProcessLauncherDelegate, #elif defined(OS_POSIX) use_zygote, - base::EnvironmentVector(), + base::EnvironmentMap(), #endif cmd_line); @@ -234,16 +210,17 @@ void WorkerProcessHost::CreateMessageFilters(int render_process_id) { net::URLRequestContextGetter* url_request_context = partition_.url_request_context(); - net::URLRequestContextGetter* media_url_request_context = - partition_.url_request_context(); + + ResourceMessageFilter::GetContextsCallback get_contexts_callback( + base::Bind(&WorkerProcessHost::GetContexts, + base::Unretained(this))); ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( - process_->GetData().id, PROCESS_TYPE_WORKER, resource_context_, + process_->GetData().id, PROCESS_TYPE_WORKER, partition_.appcache_service(), blob_storage_context, partition_.filesystem_context(), - new URLRequestContextSelector(url_request_context, - media_url_request_context)); + get_contexts_callback); process_->GetHost()->AddFilter(resource_message_filter); worker_message_filter_ = new WorkerMessageFilter( @@ -269,11 +246,15 @@ void WorkerProcessHost::CreateMessageFilters(int render_process_id) { partition_.quota_manager(), GetContentClient()->browser()->CreateQuotaPermissionContext())); + SocketStreamDispatcherHost::GetRequestContextCallback + request_context_callback( + base::Bind(&WorkerProcessHost::GetRequestContext, + base::Unretained(this))); + SocketStreamDispatcherHost* socket_stream_dispatcher_host = new SocketStreamDispatcherHost( render_process_id, - new URLRequestContextSelector(url_request_context, - media_url_request_context), + request_context_callback, resource_context_); socket_stream_dispatcher_host_ = socket_stream_dispatcher_host; process_->GetHost()->AddFilter(socket_stream_dispatcher_host); @@ -588,6 +569,18 @@ std::vector<std::pair<int, int> > WorkerProcessHost::GetRenderViewIDsForWorker( return result; } +void WorkerProcessHost::GetContexts(const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context) { + *resource_context = resource_context_; + *request_context = partition_.url_request_context()->GetURLRequestContext(); +} + +net::URLRequestContext* WorkerProcessHost::GetRequestContext( + ResourceType::Type resource_type) { + return partition_.url_request_context()->GetURLRequestContext(); +} + WorkerProcessHost::WorkerInstance::WorkerInstance( const GURL& url, const string16& name, diff --git a/chromium/content/browser/worker_host/worker_process_host.h b/chromium/content/browser/worker_host/worker_process_host.h index 226d3454f55..7bc6884e64a 100644 --- a/chromium/content/browser/worker_host/worker_process_host.h +++ b/chromium/content/browser/worker_host/worker_process_host.h @@ -20,11 +20,18 @@ #include "content/public/common/process_type.h" #include "ipc/ipc_sender.h" #include "url/gurl.h" +#include "webkit/common/resource_type.h" + +struct ResourceHostMsg_Request; namespace fileapi { class FileSystemContext; } // namespace fileapi +namespace net { +class URLRequestContext; +} + namespace webkit_database { class DatabaseTracker; } // namespace webkit_database @@ -218,6 +225,12 @@ class WorkerProcessHost : public BrowserChildProcessHostDelegate, // given worker. std::vector<std::pair<int, int> > GetRenderViewIDsForWorker(int route_id); + // Callbacks for ResourceMessageFilter and SocketStreamDispatcherHost. + void GetContexts(const ResourceHostMsg_Request& request, + ResourceContext** resource_context, + net::URLRequestContext** request_context); + net::URLRequestContext* GetRequestContext(ResourceType::Type resource_type); + Instances instances_; ResourceContext* const resource_context_; diff --git a/chromium/content/browser/worker_host/worker_service_impl.cc b/chromium/content/browser/worker_host/worker_service_impl.cc index 5fb8e62bedb..d2ab272922d 100644 --- a/chromium/content/browser/worker_host/worker_service_impl.cc +++ b/chromium/content/browser/worker_host/worker_service_impl.cc @@ -21,6 +21,7 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/web_contents.h" @@ -110,16 +111,17 @@ void WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities() { new std::set<std::pair<int, int> >(); // Gather up all the visible renderer process/view pairs - RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); - for (size_t i = 0; i < widgets.size(); ++i) { - if (widgets[i]->GetProcess()->VisibleWidgetCount() == 0) + scoped_ptr<RenderWidgetHostIterator> widgets( + RenderWidgetHost::GetRenderWidgetHosts()); + while (RenderWidgetHost* widget = widgets->GetNextHost()) { + if (widget->GetProcess()->VisibleWidgetCount() == 0) continue; - RenderWidgetHostView* render_view = widgets[i]->GetView(); + RenderWidgetHostView* render_view = widget->GetView(); if (render_view && render_view->IsShowing()) { visible_renderer_ids->insert( - std::pair<int, int>(widgets[i]->GetProcess()->GetID(), - widgets[i]->GetRoutingID())); + std::pair<int, int>(widget->GetProcess()->GetID(), + widget->GetRoutingID())); } } diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc index bb84e62ce3a..fea43b50b17 100644 --- a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc +++ b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc @@ -29,6 +29,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "content/browser/renderer_host/render_sandbox_host_linux.h" +#include "content/common/child_process_sandbox_support_impl_linux.h" #include "content/common/zygote_commands_linux.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_switches.h" @@ -36,6 +37,7 @@ #include "sandbox/linux/suid/client/setuid_sandbox_client.h" #include "sandbox/linux/suid/common/sandbox.h" #include "ui/base/ui_base_switches.h" +#include "ui/gfx/switches.h" #if defined(USE_TCMALLOC) #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" @@ -149,7 +151,7 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { // Start up the sandbox host process and get the file descriptor for the // renderers to talk to it. const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); - fds_to_map.push_back(std::make_pair(sfd, kZygoteRendererSocketFd)); + fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD())); int dummy_fd = -1; if (using_suid_sandbox_) { diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.h b/chromium/content/browser/zygote_host/zygote_host_impl_linux.h index 9027fe706e3..6a1aca5ba79 100644 --- a/chromium/content/browser/zygote_host/zygote_host_impl_linux.h +++ b/chromium/content/browser/zygote_host/zygote_host_impl_linux.h @@ -40,7 +40,11 @@ class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost { // Unfortunately the Zygote can not accurately figure out if a process // is already dead without waiting synchronously for it. // |known_dead| should be set to true when we already know that the process - // is dead. + // is dead. When |known_dead| is false, processes could be seen as + // still running, even when they're not. When |known_dead| is true, the + // process will be SIGKILL-ed first (which should have no effect if it was + // really dead). This is to prevent a waiting waitpid() from blocking in + // a single-threaded Zygote. See crbug.com/157458. base::TerminationStatus GetTerminationStatus(base::ProcessHandle handle, bool known_dead, int* exit_code); |