Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/accessibility/browser_accessibility_win.h" | 5 #include "content/browser/accessibility/browser_accessibility_win.h" |
| 6 | 6 |
| 7 #include <UIAutomationClient.h> | 7 #include <UIAutomationClient.h> |
| 8 #include <UIAutomationCoreApi.h> | 8 #include <UIAutomationCoreApi.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 2046 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2057 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) { | 2057 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) { |
| 2058 if (!instance_active()) | 2058 if (!instance_active()) |
| 2059 return E_FAIL; | 2059 return E_FAIL; |
| 2060 | 2060 |
| 2061 if (!n_selections) | 2061 if (!n_selections) |
| 2062 return E_INVALIDARG; | 2062 return E_INVALIDARG; |
| 2063 | 2063 |
| 2064 *n_selections = 0; | 2064 *n_selections = 0; |
| 2065 int selection_start, selection_end; | 2065 int selection_start, selection_end; |
| 2066 GetSelectionOffsets(&selection_start, &selection_end); | 2066 GetSelectionOffsets(&selection_start, &selection_end); |
| 2067 if (selection_start >= 0 && selection_end >= 0 && | 2067 |
| 2068 // A selection exists either: when both selection endpoints are inside this | |
| 2069 // object but their values are not equal, or when one endpoint is inside and | |
| 2070 // the other outside this object. | |
| 2071 if ((selection_start >= 0 || selection_end >= 0) && | |
| 2068 selection_start != selection_end) | 2072 selection_start != selection_end) |
| 2069 *n_selections = 1; | 2073 *n_selections = 1; |
| 2070 | 2074 |
| 2071 return S_OK; | 2075 return S_OK; |
| 2072 } | 2076 } |
| 2073 | 2077 |
| 2074 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, | 2078 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, |
| 2075 LONG* start_offset, | 2079 LONG* start_offset, |
| 2076 LONG* end_offset) { | 2080 LONG* end_offset) { |
| 2077 if (!instance_active()) | 2081 if (!instance_active()) |
| (...skipping 1563 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3641 int value; | 3645 int value; |
| 3642 if (GetIntAttribute(attribute, &value)) { | 3646 if (GetIntAttribute(attribute, &value)) { |
| 3643 win_attributes_->ia2_attributes.push_back( | 3647 win_attributes_->ia2_attributes.push_back( |
| 3644 base::ASCIIToUTF16(ia2_attr) + L":" + | 3648 base::ASCIIToUTF16(ia2_attr) + L":" + |
| 3645 base::IntToString16(value)); | 3649 base::IntToString16(value)); |
| 3646 } | 3650 } |
| 3647 } | 3651 } |
| 3648 | 3652 |
| 3649 int32 BrowserAccessibilityWin::GetHyperlinkIndexFromChild( | 3653 int32 BrowserAccessibilityWin::GetHyperlinkIndexFromChild( |
| 3650 const BrowserAccessibilityWin& child) const { | 3654 const BrowserAccessibilityWin& child) const { |
| 3655 if (hyperlinks().empty()) | |
| 3656 return -1; | |
| 3657 | |
| 3651 auto iterator = std::find( | 3658 auto iterator = std::find( |
| 3652 hyperlinks().begin(), hyperlinks().end(), child.GetId()); | 3659 hyperlinks().begin(), hyperlinks().end(), child.GetId()); |
| 3653 if (iterator == hyperlinks().end()) | 3660 if (iterator == hyperlinks().end()) |
| 3654 return -1; | 3661 return -1; |
| 3655 | 3662 |
| 3656 return static_cast<int32>(iterator - hyperlinks().begin()); | 3663 return static_cast<int32>(iterator - hyperlinks().begin()); |
| 3657 } | 3664 } |
| 3658 | 3665 |
| 3659 int32 BrowserAccessibilityWin::GetHypertextOffsetFromHyperlinkIndex( | 3666 int32 BrowserAccessibilityWin::GetHypertextOffsetFromHyperlinkIndex( |
| 3660 int32 hyperlink_index) const { | 3667 int32 hyperlink_index) const { |
| 3661 auto& offsets_map = hyperlink_offset_to_index(); | 3668 for (auto& offset_index : hyperlink_offset_to_index()) { |
| 3662 for (auto& offset_index : offsets_map) { | |
| 3663 if (offset_index.second == hyperlink_index) | 3669 if (offset_index.second == hyperlink_index) |
| 3664 return offset_index.first; | 3670 return offset_index.first; |
| 3665 } | 3671 } |
| 3666 | 3672 |
| 3667 return -1; | 3673 return -1; |
| 3668 } | 3674 } |
| 3669 | 3675 |
| 3670 int32 BrowserAccessibilityWin::GetHypertextOffsetFromChild( | 3676 int32 BrowserAccessibilityWin::GetHypertextOffsetFromChild( |
| 3671 const BrowserAccessibilityWin& child) const { | 3677 const BrowserAccessibilityWin& child) const { |
| 3672 int32 hyperlink_index = GetHyperlinkIndexFromChild(child); | 3678 int32 hyperlink_index = GetHyperlinkIndexFromChild(child); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 3683 while (parent_object && parent_object != this) { | 3689 while (parent_object && parent_object != this) { |
| 3684 current_object = parent_object; | 3690 current_object = parent_object; |
| 3685 parent_object = current_object->GetParent()->ToBrowserAccessibilityWin(); | 3691 parent_object = current_object->GetParent()->ToBrowserAccessibilityWin(); |
| 3686 } | 3692 } |
| 3687 if (!parent_object) | 3693 if (!parent_object) |
| 3688 return -1; | 3694 return -1; |
| 3689 | 3695 |
| 3690 return parent_object->GetHypertextOffsetFromChild(*current_object); | 3696 return parent_object->GetHypertextOffsetFromChild(*current_object); |
| 3691 } | 3697 } |
| 3692 | 3698 |
| 3699 int BrowserAccessibilityWin::GetHypertextOffsetFromEndpoint( | |
| 3700 const BrowserAccessibilityWin& endpoint_object, int endpoint_offset) const { | |
| 3701 // There are three cases: | |
| 3702 // 1. Either the selection endpoint is inside this object or is an ancestor of | |
| 3703 // of this object. endpoint_offset should be returned. | |
| 3704 // 2. The selection endpoint is a pure descendant of this object. The offset | |
| 3705 // of the embedded object character corresponding to the subtree in which | |
| 3706 // the endpoint is located should be returned. | |
| 3707 // 3. The selection endpoint is in a completely different part of the tree. | |
| 3708 // Either 0 or text_length should be returned depending on the direction that | |
| 3709 // one needs to travel to find the endpoint. | |
| 3710 | |
| 3711 | |
| 3712 // Case 1. | |
| 3713 | |
| 3714 // IsDescendantOf includes the case when endpoint_object == this. | |
| 3715 if (IsDescendantOf(&endpoint_object) || | |
| 3716 // Handle the case when the endpoint is a direct text-only child. | |
| 3717 // The offset should still be valid on the parent. | |
| 3718 (endpoint_object.IsTextOnlyObject() && | |
| 3719 endpoint_object.GetParent() == this)) { | |
| 3720 return endpoint_offset; | |
| 3721 } | |
| 3722 | |
|
dmazzoni
2015/09/30 20:09:56
Please remove the double blank lines within a func
| |
| 3723 | |
| 3724 const BrowserAccessibility* common_parent = this; | |
| 3725 while (common_parent && !endpoint_object.IsDescendantOf(common_parent)) { | |
| 3726 common_parent = common_parent->GetParent(); | |
| 3727 } | |
| 3728 if (!common_parent) | |
| 3729 return -1; | |
| 3730 | |
| 3731 // TODO(nektar): Add a const version of ToBrowserAccessibilityWin. | |
|
dmazzoni
2015/09/30 20:09:56
Please just do this as part of this changelist
| |
| 3732 auto common_parent_win = static_cast<const BrowserAccessibilityWin*>( | |
| 3733 common_parent); | |
| 3734 | |
| 3735 // Text only objects must have a parent. | |
| 3736 DCHECK(!IsTextOnlyObject() || GetParent()); | |
| 3737 DCHECK(!endpoint_object.IsTextOnlyObject() || endpoint_object.GetParent()); | |
| 3738 // Text only objects that are direct descendants should behave as if they | |
| 3739 // are part of their parent when computing hyperlink offsets. | |
| 3740 const BrowserAccessibilityWin* non_text_this = IsTextOnlyObject() ? | |
|
dmazzoni
2015/09/30 20:09:56
how about nearest_non_text_ancestor
| |
| 3741 GetParent()->ToBrowserAccessibilityWin() : this; | |
| 3742 const BrowserAccessibilityWin& non_text_endpoint = | |
| 3743 endpoint_object.IsTextOnlyObject() ? | |
| 3744 *(endpoint_object.GetParent()->ToBrowserAccessibilityWin()) : | |
|
dmazzoni
2015/09/30 20:09:56
I'd indent this and the next line 4 more spaces be
| |
| 3745 endpoint_object; | |
| 3746 | |
| 3747 | |
| 3748 // Case 2. | |
| 3749 | |
| 3750 // We already checked in case 1 if our endpoint is inside this object. | |
| 3751 // We can safely assume that it is a descendant or in a completely different | |
| 3752 // part of the tree. | |
| 3753 if (common_parent_win == non_text_this) | |
| 3754 return non_text_this->GetHypertextOffsetFromDescendant(non_text_endpoint); | |
| 3755 | |
| 3756 | |
| 3757 // Case 3. | |
| 3758 | |
| 3759 // We can safely assume that the endpoint is in another part of the tree or | |
| 3760 // at common parent, and that this object is a descendant of common parent. | |
| 3761 int32 current_offset = static_cast<int>( | |
|
dmazzoni
2015/09/30 20:09:56
nit: <int32> to match the type you're assigning to
| |
| 3762 common_parent_win->GetHypertextOffsetFromDescendant(*non_text_this)); | |
| 3763 DCHECK_GE(current_offset, 0); | |
| 3764 if (common_parent_win != &non_text_endpoint) { | |
| 3765 endpoint_offset = static_cast<int>( | |
| 3766 common_parent_win->GetHypertextOffsetFromDescendant(non_text_endpoint)); | |
| 3767 DCHECK_GE(endpoint_offset, 0); | |
| 3768 } | |
| 3769 | |
| 3770 if (endpoint_offset < current_offset) | |
| 3771 return 0; | |
| 3772 if (endpoint_offset > current_offset) | |
| 3773 return TextForIAccessibleText().length(); | |
| 3774 | |
| 3775 NOTREACHED(); | |
| 3776 return -1; | |
| 3777 } | |
| 3778 | |
| 3693 int BrowserAccessibilityWin::GetSelectionAnchor() const { | 3779 int BrowserAccessibilityWin::GetSelectionAnchor() const { |
| 3694 BrowserAccessibility* root = manager()->GetRoot(); | 3780 BrowserAccessibility* root = manager()->GetRoot(); |
| 3695 int32 anchor_id; | 3781 int32 anchor_id; |
| 3696 if (!root || !root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, &anchor_id)) | 3782 if (!root || !root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, &anchor_id)) |
| 3697 return -1; | 3783 return -1; |
| 3698 | 3784 |
| 3699 BrowserAccessibilityWin* anchor_object = manager()->GetFromID( | 3785 BrowserAccessibilityWin* anchor_object = manager()->GetFromID( |
| 3700 anchor_id)->ToBrowserAccessibilityWin(); | 3786 anchor_id)->ToBrowserAccessibilityWin(); |
| 3701 if (!anchor_object) | 3787 if (!anchor_object) |
| 3702 return -1; | 3788 return -1; |
| 3703 | 3789 |
| 3704 // Includes the case when anchor_object == this. | 3790 int anchor_offset; |
| 3705 if (IsDescendantOf(anchor_object) || | 3791 if (!root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, &anchor_offset)) |
| 3706 // Text only objects that are direct descendants should behave as if they | 3792 return -1; |
| 3707 // are part of this object when computing hypertext. | |
| 3708 (anchor_object->GetParent() == this && | |
| 3709 anchor_object->IsTextOnlyObject())) { | |
| 3710 int anchor_offset; | |
| 3711 if (!root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, &anchor_offset)) | |
| 3712 return -1; | |
| 3713 | 3793 |
| 3714 return anchor_offset; | 3794 return GetHypertextOffsetFromEndpoint(*anchor_object, anchor_offset); |
| 3715 } | |
| 3716 | |
| 3717 if (anchor_object->IsDescendantOf(this)) | |
| 3718 return GetHypertextOffsetFromDescendant(*anchor_object); | |
| 3719 | |
| 3720 return -1; | |
| 3721 } | 3795 } |
| 3722 | 3796 |
| 3723 int BrowserAccessibilityWin::GetSelectionFocus() const { | 3797 int BrowserAccessibilityWin::GetSelectionFocus() const { |
| 3724 BrowserAccessibility* root = manager()->GetRoot(); | 3798 BrowserAccessibility* root = manager()->GetRoot(); |
| 3725 int32 focus_id; | 3799 int32 focus_id; |
| 3726 if (!root || !root->GetIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, &focus_id)) | 3800 if (!root || !root->GetIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, &focus_id)) |
| 3727 return -1; | 3801 return -1; |
| 3728 | 3802 |
| 3729 BrowserAccessibilityWin* focus_object = manager()->GetFromID( | 3803 BrowserAccessibilityWin* focus_object = manager()->GetFromID( |
| 3730 focus_id)->ToBrowserAccessibilityWin(); | 3804 focus_id)->ToBrowserAccessibilityWin(); |
| 3731 if (!focus_object) | 3805 if (!focus_object) |
| 3732 return -1; | 3806 return -1; |
| 3733 | 3807 |
| 3734 // Includes the case when focus_object == this. | 3808 int focus_offset; |
| 3735 if (IsDescendantOf(focus_object) || | 3809 if (!root->GetIntAttribute(ui::AX_ATTR_FOCUS_OFFSET, &focus_offset)) |
| 3736 // Text only objects that are direct descendants should behave as if they | 3810 return -1; |
| 3737 // are part of this object when computing hypertext. | |
| 3738 (focus_object->GetParent() == this && focus_object->IsTextOnlyObject())) { | |
| 3739 int focus_offset; | |
| 3740 if (!root->GetIntAttribute(ui::AX_ATTR_FOCUS_OFFSET, &focus_offset)) | |
| 3741 return -1; | |
| 3742 | 3811 |
| 3743 return focus_offset; | 3812 return GetHypertextOffsetFromEndpoint(*focus_object, focus_offset); |
| 3744 } | |
| 3745 | |
| 3746 if (focus_object->IsDescendantOf(this)) | |
| 3747 return GetHypertextOffsetFromDescendant(*focus_object); | |
| 3748 | |
| 3749 return -1; | |
| 3750 } | 3813 } |
| 3751 | 3814 |
| 3752 void BrowserAccessibilityWin::GetSelectionOffsets( | 3815 void BrowserAccessibilityWin::GetSelectionOffsets( |
| 3753 int* selection_start, int* selection_end) const { | 3816 int* selection_start, int* selection_end) const { |
| 3754 DCHECK(selection_start && selection_end); | 3817 DCHECK(selection_start && selection_end); |
| 3755 | 3818 |
| 3756 if (IsEditableText() && !HasState(ui::AX_STATE_RICHLY_EDITABLE) && | 3819 if (IsEditableText() && !HasState(ui::AX_STATE_RICHLY_EDITABLE) && |
| 3757 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, selection_start) && | 3820 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, selection_start) && |
| 3758 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, selection_end)) { | 3821 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, selection_end)) { |
| 3759 return; | 3822 return; |
| 3760 } | 3823 } |
| 3761 | 3824 |
| 3762 *selection_start = GetSelectionAnchor(); | 3825 *selection_start = GetSelectionAnchor(); |
| 3763 *selection_end = GetSelectionFocus(); | 3826 *selection_end = GetSelectionFocus(); |
| 3764 if (*selection_start < 0 || *selection_end < 0) | 3827 if (*selection_start < 0 || *selection_end < 0) |
| 3765 return; | 3828 return; |
| 3766 | 3829 |
| 3830 // If the selection is collapsed, then return the caret position only if the | |
| 3831 // caret is active on this object. | |
| 3832 // The focus object indicates the caret position, but we will use the anchor | |
| 3833 // for consistency with Firefox. | |
| 3834 // TODO(nektar): Investigate why Firefox uses the anchor and not the focus. | |
| 3835 if (*selection_start == *selection_end) { | |
| 3836 BrowserAccessibility* root = manager()->GetRoot(); | |
| 3837 int32 anchor_id; | |
| 3838 if (!root || !root->GetIntAttribute( | |
| 3839 ui::AX_ATTR_ANCHOR_OBJECT_ID, &anchor_id)) { | |
| 3840 return; | |
| 3841 } | |
| 3842 | |
| 3843 BrowserAccessibilityWin* anchor_object = manager()->GetFromID( | |
| 3844 anchor_id)->ToBrowserAccessibilityWin(); | |
| 3845 if (!anchor_object) | |
| 3846 return; | |
| 3847 | |
| 3848 if (!anchor_object->IsDescendantOf(this) && | |
| 3849 (IsTextOnlyObject() && GetParent() != anchor_object)) { | |
| 3850 *selection_start = -1; | |
| 3851 *selection_end = -1; | |
| 3852 return; | |
| 3853 } | |
| 3854 } | |
| 3855 | |
| 3767 if (*selection_end < *selection_start) | 3856 if (*selection_end < *selection_start) |
| 3768 std::swap(*selection_start, *selection_end); | 3857 std::swap(*selection_start, *selection_end); |
| 3769 | 3858 |
| 3770 // IA2 Spec says that the end of the selection should be after the last | 3859 // The IA2 Spec says that if a selection is present and if the end of the |
| 3771 // embedded object character that is part of the selection, if there is one. | 3860 // selection falls on an embedded object character, it should be incremented |
| 3772 if (hyperlink_offset_to_index().find(*selection_end) != | 3861 // by one so that it points after the embedded object character. |
| 3773 hyperlink_offset_to_index().end()) { | 3862 LONG hyperlink_index; |
| 3863 auto current_object = const_cast<BrowserAccessibilityWin*>(this); | |
| 3864 HRESULT hr = current_object->get_hyperlinkIndex( | |
| 3865 *selection_end, &hyperlink_index); | |
| 3866 if (hr != S_OK) | |
| 3867 return; | |
| 3868 | |
| 3869 base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink; | |
| 3870 hr = current_object->get_hyperlink( | |
| 3871 hyperlink_index, hyperlink.Receive()); | |
| 3872 DCHECK(SUCCEEDED(hr)); | |
| 3873 base::win::ScopedComPtr<IAccessibleText> ax_hyperlink; | |
| 3874 hr = hyperlink.QueryInterface(ax_hyperlink.Receive()); | |
| 3875 DCHECK(SUCCEEDED(hr)); | |
| 3876 LONG nSelections = -1; | |
| 3877 hr = ax_hyperlink->get_nSelections(&nSelections); | |
| 3878 DCHECK(SUCCEEDED(hr)); | |
| 3879 | |
| 3880 if (nSelections > 0) | |
| 3774 ++(*selection_end); | 3881 ++(*selection_end); |
| 3775 } | |
| 3776 } | 3882 } |
| 3777 | 3883 |
| 3778 base::string16 BrowserAccessibilityWin::GetNameRecursive() const { | 3884 base::string16 BrowserAccessibilityWin::GetNameRecursive() const { |
| 3779 if (!name().empty()) { | 3885 if (!name().empty()) { |
| 3780 return name(); | 3886 return name(); |
| 3781 } | 3887 } |
| 3782 | 3888 |
| 3783 base::string16 result; | 3889 base::string16 result; |
| 3784 for (uint32 i = 0; i < PlatformChildCount(); ++i) { | 3890 for (uint32 i = 0; i < PlatformChildCount(); ++i) { |
| 3785 result += PlatformGetChild(i)->ToBrowserAccessibilityWin()-> | 3891 result += PlatformGetChild(i)->ToBrowserAccessibilityWin()-> |
| 3786 GetNameRecursive(); | 3892 GetNameRecursive(); |
| 3787 } | 3893 } |
| 3788 return result; | 3894 return result; |
| 3789 } | 3895 } |
| 3790 | 3896 |
| 3791 base::string16 BrowserAccessibilityWin::GetValueText() { | 3897 base::string16 BrowserAccessibilityWin::GetValueText() { |
| 3792 float fval; | 3898 float fval; |
| 3793 base::string16 value = this->value(); | 3899 base::string16 value = this->value(); |
| 3794 | 3900 |
| 3795 if (value.empty() && | 3901 if (value.empty() && |
| 3796 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { | 3902 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { |
| 3797 value = base::UTF8ToUTF16(base::DoubleToString(fval)); | 3903 value = base::UTF8ToUTF16(base::DoubleToString(fval)); |
| 3798 } | 3904 } |
| 3799 return value; | 3905 return value; |
| 3800 } | 3906 } |
| 3801 | 3907 |
| 3802 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() { | 3908 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() const { |
| 3803 switch (GetRole()) { | 3909 switch (GetRole()) { |
| 3804 case ui::AX_ROLE_TEXT_FIELD: | 3910 case ui::AX_ROLE_TEXT_FIELD: |
| 3805 case ui::AX_ROLE_MENU_LIST_OPTION: | 3911 case ui::AX_ROLE_MENU_LIST_OPTION: |
| 3806 return value(); | 3912 return value(); |
| 3807 default: | 3913 default: |
| 3808 return hypertext(); | 3914 return hypertext(); |
| 3809 } | 3915 } |
| 3810 } | 3916 } |
| 3811 | 3917 |
| 3812 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index, | 3918 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index, |
| (...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4499 ia2_role = ia_role; | 4605 ia2_role = ia_role; |
| 4500 | 4606 |
| 4501 win_attributes_->ia_role = ia_role; | 4607 win_attributes_->ia_role = ia_role; |
| 4502 win_attributes_->ia_state = ia_state; | 4608 win_attributes_->ia_state = ia_state; |
| 4503 win_attributes_->role_name = role_name; | 4609 win_attributes_->role_name = role_name; |
| 4504 win_attributes_->ia2_role = ia2_role; | 4610 win_attributes_->ia2_role = ia2_role; |
| 4505 win_attributes_->ia2_state = ia2_state; | 4611 win_attributes_->ia2_state = ia2_state; |
| 4506 } | 4612 } |
| 4507 | 4613 |
| 4508 } // namespace content | 4614 } // namespace content |
| OLD | NEW |