| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 /* | 5 /* |
| 6 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | 6 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
| 7 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) | 7 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
| 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. | 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |
| 9 * (http://www.torchmobile.com/) | 9 * (http://www.torchmobile.com/) |
| 10 * | 10 * |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 34 */ | 34 */ |
| 35 | 35 |
| 36 #include "content/browser/frame_host/navigation_controller_impl.h" | 36 #include "content/browser/frame_host/navigation_controller_impl.h" |
| 37 | 37 |
| 38 #include <utility> |
| 39 |
| 38 #include "base/bind.h" | 40 #include "base/bind.h" |
| 39 #include "base/command_line.h" | 41 #include "base/command_line.h" |
| 40 #include "base/logging.h" | 42 #include "base/logging.h" |
| 41 #include "base/metrics/histogram.h" | 43 #include "base/metrics/histogram.h" |
| 42 #include "base/strings/string_number_conversions.h" // Temporary | 44 #include "base/strings/string_number_conversions.h" // Temporary |
| 43 #include "base/strings/string_util.h" | 45 #include "base/strings/string_util.h" |
| 44 #include "base/strings/utf_string_conversions.h" | 46 #include "base/strings/utf_string_conversions.h" |
| 45 #include "base/time/time.h" | 47 #include "base/time/time.h" |
| 46 #include "base/trace_event/trace_event.h" | 48 #include "base/trace_event/trace_event.h" |
| 47 #include "build/build_config.h" | 49 #include "build/build_config.h" |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 RestoreType type, | 273 RestoreType type, |
| 272 std::vector<scoped_ptr<NavigationEntry>>* entries) { | 274 std::vector<scoped_ptr<NavigationEntry>>* entries) { |
| 273 // Verify that this controller is unused and that the input is valid. | 275 // Verify that this controller is unused and that the input is valid. |
| 274 DCHECK(GetEntryCount() == 0 && !GetPendingEntry()); | 276 DCHECK(GetEntryCount() == 0 && !GetPendingEntry()); |
| 275 DCHECK(selected_navigation >= 0 && | 277 DCHECK(selected_navigation >= 0 && |
| 276 selected_navigation < static_cast<int>(entries->size())); | 278 selected_navigation < static_cast<int>(entries->size())); |
| 277 | 279 |
| 278 needs_reload_ = true; | 280 needs_reload_ = true; |
| 279 entries_.reserve(entries->size()); | 281 entries_.reserve(entries->size()); |
| 280 for (auto& entry : *entries) | 282 for (auto& entry : *entries) |
| 281 entries_.push_back(NavigationEntryImpl::FromNavigationEntry(entry.Pass())); | 283 entries_.push_back( |
| 284 NavigationEntryImpl::FromNavigationEntry(std::move(entry))); |
| 282 | 285 |
| 283 // At this point, the |entries| is full of empty scoped_ptrs, so it can be | 286 // At this point, the |entries| is full of empty scoped_ptrs, so it can be |
| 284 // cleared out safely. | 287 // cleared out safely. |
| 285 entries->clear(); | 288 entries->clear(); |
| 286 | 289 |
| 287 // And finish the restore. | 290 // And finish the restore. |
| 288 FinishRestore(selected_navigation, type); | 291 FinishRestore(selected_navigation, type); |
| 289 } | 292 } |
| 290 | 293 |
| 291 void NavigationControllerImpl::Reload(bool check_for_repost) { | 294 void NavigationControllerImpl::Reload(bool check_for_repost) { |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 NavigationControllerImpl::GetEntryWithUniqueID(int nav_entry_id) const { | 443 NavigationControllerImpl::GetEntryWithUniqueID(int nav_entry_id) const { |
| 441 int index = GetEntryIndexWithUniqueID(nav_entry_id); | 444 int index = GetEntryIndexWithUniqueID(nav_entry_id); |
| 442 return (index != -1) ? entries_[index].get() : nullptr; | 445 return (index != -1) ? entries_[index].get() : nullptr; |
| 443 } | 446 } |
| 444 | 447 |
| 445 void NavigationControllerImpl::LoadEntry( | 448 void NavigationControllerImpl::LoadEntry( |
| 446 scoped_ptr<NavigationEntryImpl> entry) { | 449 scoped_ptr<NavigationEntryImpl> entry) { |
| 447 // When navigating to a new page, we don't know for sure if we will actually | 450 // When navigating to a new page, we don't know for sure if we will actually |
| 448 // end up leaving the current page. The new page load could for example | 451 // end up leaving the current page. The new page load could for example |
| 449 // result in a download or a 'no content' response (e.g., a mailto: URL). | 452 // result in a download or a 'no content' response (e.g., a mailto: URL). |
| 450 SetPendingEntry(entry.Pass()); | 453 SetPendingEntry(std::move(entry)); |
| 451 NavigateToPendingEntry(NO_RELOAD); | 454 NavigateToPendingEntry(NO_RELOAD); |
| 452 } | 455 } |
| 453 | 456 |
| 454 void NavigationControllerImpl::SetPendingEntry( | 457 void NavigationControllerImpl::SetPendingEntry( |
| 455 scoped_ptr<NavigationEntryImpl> entry) { | 458 scoped_ptr<NavigationEntryImpl> entry) { |
| 456 DiscardNonCommittedEntriesInternal(); | 459 DiscardNonCommittedEntriesInternal(); |
| 457 pending_entry_ = entry.release(); | 460 pending_entry_ = entry.release(); |
| 458 NotificationService::current()->Notify( | 461 NotificationService::current()->Notify( |
| 459 NOTIFICATION_NAV_ENTRY_PENDING, | 462 NOTIFICATION_NAV_ENTRY_PENDING, |
| 460 Source<NavigationController>(this), | 463 Source<NavigationController>(this), |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 return GetCurrentEntryIndex() + offset; | 555 return GetCurrentEntryIndex() + offset; |
| 553 } | 556 } |
| 554 | 557 |
| 555 void NavigationControllerImpl::TakeScreenshot() { | 558 void NavigationControllerImpl::TakeScreenshot() { |
| 556 screenshot_manager_->TakeScreenshot(); | 559 screenshot_manager_->TakeScreenshot(); |
| 557 } | 560 } |
| 558 | 561 |
| 559 void NavigationControllerImpl::SetScreenshotManager( | 562 void NavigationControllerImpl::SetScreenshotManager( |
| 560 scoped_ptr<NavigationEntryScreenshotManager> manager) { | 563 scoped_ptr<NavigationEntryScreenshotManager> manager) { |
| 561 if (manager.get()) | 564 if (manager.get()) |
| 562 screenshot_manager_ = manager.Pass(); | 565 screenshot_manager_ = std::move(manager); |
| 563 else | 566 else |
| 564 screenshot_manager_.reset(new NavigationEntryScreenshotManager(this)); | 567 screenshot_manager_.reset(new NavigationEntryScreenshotManager(this)); |
| 565 } | 568 } |
| 566 | 569 |
| 567 bool NavigationControllerImpl::CanGoBack() const { | 570 bool NavigationControllerImpl::CanGoBack() const { |
| 568 return CanGoToOffset(-1); | 571 return CanGoToOffset(-1); |
| 569 } | 572 } |
| 570 | 573 |
| 571 bool NavigationControllerImpl::CanGoForward() const { | 574 bool NavigationControllerImpl::CanGoForward() const { |
| 572 return CanGoToOffset(1); | 575 return CanGoToOffset(1); |
| (...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 809 #if defined(OS_ANDROID) | 812 #if defined(OS_ANDROID) |
| 810 entry->SetDataURLAsString(params.data_url_as_string); | 813 entry->SetDataURLAsString(params.data_url_as_string); |
| 811 #endif | 814 #endif |
| 812 entry->SetCanLoadLocalResources(params.can_load_local_resources); | 815 entry->SetCanLoadLocalResources(params.can_load_local_resources); |
| 813 break; | 816 break; |
| 814 default: | 817 default: |
| 815 NOTREACHED(); | 818 NOTREACHED(); |
| 816 break; | 819 break; |
| 817 }; | 820 }; |
| 818 | 821 |
| 819 LoadEntry(entry.Pass()); | 822 LoadEntry(std::move(entry)); |
| 820 } | 823 } |
| 821 | 824 |
| 822 bool NavigationControllerImpl::RendererDidNavigate( | 825 bool NavigationControllerImpl::RendererDidNavigate( |
| 823 RenderFrameHostImpl* rfh, | 826 RenderFrameHostImpl* rfh, |
| 824 const FrameHostMsg_DidCommitProvisionalLoad_Params& params, | 827 const FrameHostMsg_DidCommitProvisionalLoad_Params& params, |
| 825 LoadCommittedDetails* details) { | 828 LoadCommittedDetails* details) { |
| 826 is_initial_navigation_ = false; | 829 is_initial_navigation_ = false; |
| 827 | 830 |
| 828 // Save the previous state before we clobber it. | 831 // Save the previous state before we clobber it. |
| 829 if (GetLastCommittedEntry()) { | 832 if (GetLastCommittedEntry()) { |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1153 DCHECK(!params.history_list_was_cleared || !replace_entry); | 1156 DCHECK(!params.history_list_was_cleared || !replace_entry); |
| 1154 // The browser requested to clear the session history when it initiated the | 1157 // The browser requested to clear the session history when it initiated the |
| 1155 // navigation. Now we know that the renderer has updated its state accordingly | 1158 // navigation. Now we know that the renderer has updated its state accordingly |
| 1156 // and it is safe to also clear the browser side history. | 1159 // and it is safe to also clear the browser side history. |
| 1157 if (params.history_list_was_cleared) { | 1160 if (params.history_list_was_cleared) { |
| 1158 DiscardNonCommittedEntriesInternal(); | 1161 DiscardNonCommittedEntriesInternal(); |
| 1159 entries_.clear(); | 1162 entries_.clear(); |
| 1160 last_committed_entry_index_ = -1; | 1163 last_committed_entry_index_ = -1; |
| 1161 } | 1164 } |
| 1162 | 1165 |
| 1163 InsertOrReplaceEntry(new_entry.Pass(), replace_entry); | 1166 InsertOrReplaceEntry(std::move(new_entry), replace_entry); |
| 1164 } | 1167 } |
| 1165 | 1168 |
| 1166 void NavigationControllerImpl::RendererDidNavigateToExistingPage( | 1169 void NavigationControllerImpl::RendererDidNavigateToExistingPage( |
| 1167 RenderFrameHostImpl* rfh, | 1170 RenderFrameHostImpl* rfh, |
| 1168 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { | 1171 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { |
| 1169 // We should only get here for main frame navigations. | 1172 // We should only get here for main frame navigations. |
| 1170 DCHECK(!rfh->GetParent()); | 1173 DCHECK(!rfh->GetParent()); |
| 1171 | 1174 |
| 1172 NavigationEntryImpl* entry; | 1175 NavigationEntryImpl* entry; |
| 1173 if (params.intended_as_new_entry) { | 1176 if (params.intended_as_new_entry) { |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1282 | 1285 |
| 1283 // TODO(creis): Make sure the last committed entry always has the subframe | 1286 // TODO(creis): Make sure the last committed entry always has the subframe |
| 1284 // entry to replace, and CHECK(frame_entry->HasOneRef). For now, we might | 1287 // entry to replace, and CHECK(frame_entry->HasOneRef). For now, we might |
| 1285 // not find the entry to replace, and new_entry will be deleted when it goes | 1288 // not find the entry to replace, and new_entry will be deleted when it goes |
| 1286 // out of scope. See https://crbug.com/522193. | 1289 // out of scope. See https://crbug.com/522193. |
| 1287 } else { | 1290 } else { |
| 1288 new_entry = GetLastCommittedEntry()->Clone(); | 1291 new_entry = GetLastCommittedEntry()->Clone(); |
| 1289 } | 1292 } |
| 1290 | 1293 |
| 1291 new_entry->SetPageID(params.page_id); | 1294 new_entry->SetPageID(params.page_id); |
| 1292 InsertOrReplaceEntry(new_entry.Pass(), false); | 1295 InsertOrReplaceEntry(std::move(new_entry), false); |
| 1293 } | 1296 } |
| 1294 | 1297 |
| 1295 bool NavigationControllerImpl::RendererDidNavigateAutoSubframe( | 1298 bool NavigationControllerImpl::RendererDidNavigateAutoSubframe( |
| 1296 RenderFrameHostImpl* rfh, | 1299 RenderFrameHostImpl* rfh, |
| 1297 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { | 1300 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { |
| 1298 DCHECK(ui::PageTransitionCoreTypeIs(params.transition, | 1301 DCHECK(ui::PageTransitionCoreTypeIs(params.transition, |
| 1299 ui::PAGE_TRANSITION_AUTO_SUBFRAME)); | 1302 ui::PAGE_TRANSITION_AUTO_SUBFRAME)); |
| 1300 | 1303 |
| 1301 // We're guaranteed to have a previously committed entry, and we now need to | 1304 // We're guaranteed to have a previously committed entry, and we now need to |
| 1302 // handle navigation inside of a subframe in it without creating a new entry. | 1305 // handle navigation inside of a subframe in it without creating a new entry. |
| (...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1676 entry->set_unique_id(pending_entry_->GetUniqueID()); | 1679 entry->set_unique_id(pending_entry_->GetUniqueID()); |
| 1677 | 1680 |
| 1678 DiscardNonCommittedEntriesInternal(); | 1681 DiscardNonCommittedEntriesInternal(); |
| 1679 | 1682 |
| 1680 int current_size = static_cast<int>(entries_.size()); | 1683 int current_size = static_cast<int>(entries_.size()); |
| 1681 | 1684 |
| 1682 // When replacing, don't prune the forward history. | 1685 // When replacing, don't prune the forward history. |
| 1683 if (replace && current_size > 0) { | 1686 if (replace && current_size > 0) { |
| 1684 int32_t page_id = entry->GetPageID(); | 1687 int32_t page_id = entry->GetPageID(); |
| 1685 | 1688 |
| 1686 entries_[last_committed_entry_index_] = entry.Pass(); | 1689 entries_[last_committed_entry_index_] = std::move(entry); |
| 1687 | 1690 |
| 1688 // This is a new page ID, so we need everybody to know about it. | 1691 // This is a new page ID, so we need everybody to know about it. |
| 1689 delegate_->UpdateMaxPageID(page_id); | 1692 delegate_->UpdateMaxPageID(page_id); |
| 1690 return; | 1693 return; |
| 1691 } | 1694 } |
| 1692 | 1695 |
| 1693 // We shouldn't see replace == true when there's no committed entries. | 1696 // We shouldn't see replace == true when there's no committed entries. |
| 1694 DCHECK(!replace); | 1697 DCHECK(!replace); |
| 1695 | 1698 |
| 1696 if (current_size > 0) { | 1699 if (current_size > 0) { |
| 1697 // Prune any entries which are in front of the current entry. | 1700 // Prune any entries which are in front of the current entry. |
| 1698 // last_committed_entry_index_ must be updated here since calls to | 1701 // last_committed_entry_index_ must be updated here since calls to |
| 1699 // NotifyPrunedEntries() below may re-enter and we must make sure | 1702 // NotifyPrunedEntries() below may re-enter and we must make sure |
| 1700 // last_committed_entry_index_ is not left in an invalid state. | 1703 // last_committed_entry_index_ is not left in an invalid state. |
| 1701 int num_pruned = 0; | 1704 int num_pruned = 0; |
| 1702 while (last_committed_entry_index_ < (current_size - 1)) { | 1705 while (last_committed_entry_index_ < (current_size - 1)) { |
| 1703 num_pruned++; | 1706 num_pruned++; |
| 1704 entries_.pop_back(); | 1707 entries_.pop_back(); |
| 1705 current_size--; | 1708 current_size--; |
| 1706 } | 1709 } |
| 1707 if (num_pruned > 0) // Only notify if we did prune something. | 1710 if (num_pruned > 0) // Only notify if we did prune something. |
| 1708 NotifyPrunedEntries(this, false, num_pruned); | 1711 NotifyPrunedEntries(this, false, num_pruned); |
| 1709 } | 1712 } |
| 1710 | 1713 |
| 1711 PruneOldestEntryIfFull(); | 1714 PruneOldestEntryIfFull(); |
| 1712 | 1715 |
| 1713 int32_t page_id = entry->GetPageID(); | 1716 int32_t page_id = entry->GetPageID(); |
| 1714 entries_.push_back(entry.Pass()); | 1717 entries_.push_back(std::move(entry)); |
| 1715 last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1; | 1718 last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1; |
| 1716 | 1719 |
| 1717 // This is a new page ID, so we need everybody to know about it. | 1720 // This is a new page ID, so we need everybody to know about it. |
| 1718 delegate_->UpdateMaxPageID(page_id); | 1721 delegate_->UpdateMaxPageID(page_id); |
| 1719 } | 1722 } |
| 1720 | 1723 |
| 1721 void NavigationControllerImpl::PruneOldestEntryIfFull() { | 1724 void NavigationControllerImpl::PruneOldestEntryIfFull() { |
| 1722 if (entries_.size() >= max_entry_count()) { | 1725 if (entries_.size() >= max_entry_count()) { |
| 1723 DCHECK_EQ(max_entry_count(), entries_.size()); | 1726 DCHECK_EQ(max_entry_count(), entries_.size()); |
| 1724 DCHECK_GT(last_committed_entry_index_, 0); | 1727 DCHECK_GT(last_committed_entry_index_, 0); |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2017 } | 2020 } |
| 2018 | 2021 |
| 2019 void NavigationControllerImpl::SetTransientEntry( | 2022 void NavigationControllerImpl::SetTransientEntry( |
| 2020 scoped_ptr<NavigationEntry> entry) { | 2023 scoped_ptr<NavigationEntry> entry) { |
| 2021 // Discard any current transient entry, we can only have one at a time. | 2024 // Discard any current transient entry, we can only have one at a time. |
| 2022 int index = 0; | 2025 int index = 0; |
| 2023 if (last_committed_entry_index_ != -1) | 2026 if (last_committed_entry_index_ != -1) |
| 2024 index = last_committed_entry_index_ + 1; | 2027 index = last_committed_entry_index_ + 1; |
| 2025 DiscardTransientEntry(); | 2028 DiscardTransientEntry(); |
| 2026 entries_.insert(entries_.begin() + index, | 2029 entries_.insert(entries_.begin() + index, |
| 2027 NavigationEntryImpl::FromNavigationEntry(entry.Pass())); | 2030 NavigationEntryImpl::FromNavigationEntry(std::move(entry))); |
| 2028 transient_entry_index_ = index; | 2031 transient_entry_index_ = index; |
| 2029 delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL); | 2032 delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL); |
| 2030 } | 2033 } |
| 2031 | 2034 |
| 2032 void NavigationControllerImpl::InsertEntriesFrom( | 2035 void NavigationControllerImpl::InsertEntriesFrom( |
| 2033 const NavigationControllerImpl& source, | 2036 const NavigationControllerImpl& source, |
| 2034 int max_index) { | 2037 int max_index) { |
| 2035 DCHECK_LE(max_index, source.GetEntryCount()); | 2038 DCHECK_LE(max_index, source.GetEntryCount()); |
| 2036 size_t insert_index = 0; | 2039 size_t insert_index = 0; |
| 2037 for (int i = 0; i < max_index; i++) { | 2040 for (int i = 0; i < max_index; i++) { |
| 2038 // When cloning a tab, copy all entries except interstitial pages. | 2041 // When cloning a tab, copy all entries except interstitial pages. |
| 2039 if (source.entries_[i]->GetPageType() != PAGE_TYPE_INTERSTITIAL) { | 2042 if (source.entries_[i]->GetPageType() != PAGE_TYPE_INTERSTITIAL) { |
| 2040 // TODO(creis): Once we start sharing FrameNavigationEntries between | 2043 // TODO(creis): Once we start sharing FrameNavigationEntries between |
| 2041 // NavigationEntries, it will not be safe to share them with another tab. | 2044 // NavigationEntries, it will not be safe to share them with another tab. |
| 2042 // Must have a version of Clone that recreates them. | 2045 // Must have a version of Clone that recreates them. |
| 2043 entries_.insert(entries_.begin() + insert_index++, | 2046 entries_.insert(entries_.begin() + insert_index++, |
| 2044 source.entries_[i]->Clone().Pass()); | 2047 source.entries_[i]->Clone()); |
| 2045 } | 2048 } |
| 2046 } | 2049 } |
| 2047 } | 2050 } |
| 2048 | 2051 |
| 2049 void NavigationControllerImpl::SetGetTimestampCallbackForTest( | 2052 void NavigationControllerImpl::SetGetTimestampCallbackForTest( |
| 2050 const base::Callback<base::Time()>& get_timestamp_callback) { | 2053 const base::Callback<base::Time()>& get_timestamp_callback) { |
| 2051 get_timestamp_callback_ = get_timestamp_callback; | 2054 get_timestamp_callback_ = get_timestamp_callback; |
| 2052 } | 2055 } |
| 2053 | 2056 |
| 2054 } // namespace content | 2057 } // namespace content |
| OLD | NEW |