Commit 19614829 authored by Caroline Rising's avatar Caroline Rising Committed by Commit Bot

Add a mojom API and ReadLaterPageHandler for the Read Later feature.

Create new mojom API to communicate between web ui and the browser
process. Add ReadLaterPage handler which implements the read_later.mojom
interface and ReadLaterUI.

Bug: 1109316
Change-Id: Icc9b567bc7b1486f9a295c6e8f66a843190ee912
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2349981
Commit-Queue: Caroline Rising <corising@chromium.org>
Reviewed-by: default avatarMatthew Denton <mpdenton@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Reviewed-by: default avatarRebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800198}
parent bdbbd722
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ssl/insecure_sensitive_input_driver_factory.h" #include "chrome/browser/ssl/insecure_sensitive_input_driver_factory.h"
#include "chrome/browser/ssl/security_state_tab_helper.h" #include "chrome/browser/ssl/security_state_tab_helper.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom.h" #include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom.h"
#include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h" #include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h"
#include "chrome/browser/ui/webui/engagement/site_engagement_ui.h" #include "chrome/browser/ui/webui/engagement/site_engagement_ui.h"
...@@ -119,6 +120,8 @@ ...@@ -119,6 +120,8 @@
#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h" #include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
#include "chrome/browser/ui/webui/read_later/read_later.mojom.h"
#include "chrome/browser/ui/webui/read_later/read_later_ui.h"
#include "chrome/browser/ui/webui/tab_search/tab_search.mojom.h" #include "chrome/browser/ui/webui/tab_search/tab_search.mojom.h"
#include "chrome/browser/ui/webui/tab_search/tab_search_ui.h" #include "chrome/browser/ui/webui/tab_search/tab_search_ui.h"
#include "chrome/common/caption.mojom.h" #include "chrome/common/caption.mojom.h"
...@@ -528,6 +531,11 @@ void PopulateChromeWebUIFrameBinders( ...@@ -528,6 +531,11 @@ void PopulateChromeWebUIFrameBinders(
RegisterWebUIControllerInterfaceBinder<media_feeds::mojom::MediaFeedsStore, RegisterWebUIControllerInterfaceBinder<media_feeds::mojom::MediaFeedsStore,
MediaFeedsUI>(map); MediaFeedsUI>(map);
if (base::FeatureList::IsEnabled(features::kReadLater)) {
RegisterWebUIControllerInterfaceBinder<
read_later::mojom::PageHandlerFactory, ReadLaterUI>(map);
}
RegisterWebUIControllerInterfaceBinder<tab_search::mojom::PageHandlerFactory, RegisterWebUIControllerInterfaceBinder<tab_search::mojom::PageHandlerFactory,
TabSearchUI>(map); TabSearchUI>(map);
......
...@@ -386,6 +386,7 @@ static_library("ui") { ...@@ -386,6 +386,7 @@ static_library("ui") {
"//chrome/browser/ui/webui/interventions_internals:mojo_bindings", "//chrome/browser/ui/webui/interventions_internals:mojo_bindings",
"//chrome/browser/ui/webui/new_tab_page:mojo_bindings", "//chrome/browser/ui/webui/new_tab_page:mojo_bindings",
"//chrome/browser/ui/webui/omnibox:mojo_bindings", "//chrome/browser/ui/webui/omnibox:mojo_bindings",
"//chrome/browser/ui/webui/read_later:mojo_bindings",
"//chrome/browser/ui/webui/tab_search:mojo_bindings", "//chrome/browser/ui/webui/tab_search:mojo_bindings",
"//chrome/browser/ui/webui/usb_internals:mojo_bindings", "//chrome/browser/ui/webui/usb_internals:mojo_bindings",
"//chrome/common", "//chrome/common",
...@@ -1387,6 +1388,10 @@ static_library("ui") { ...@@ -1387,6 +1388,10 @@ static_library("ui") {
"webui/policy_indicator_localized_strings_provider.h", "webui/policy_indicator_localized_strings_provider.h",
"webui/profile_info_watcher.cc", "webui/profile_info_watcher.cc",
"webui/profile_info_watcher.h", "webui/profile_info_watcher.h",
"webui/read_later/read_later_page_handler.cc",
"webui/read_later/read_later_page_handler.h",
"webui/read_later/read_later_ui.cc",
"webui/read_later/read_later_ui.h",
"webui/sanitized_image_source.cc", "webui/sanitized_image_source.cc",
"webui/sanitized_image_source.h", "webui/sanitized_image_source.h",
"webui/settings/about_handler.cc", "webui/settings/about_handler.cc",
......
// Copyright 2020 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 "chrome/browser/ui/read_later/read_later_test_utils.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "components/reading_list/core/reading_list_model.h"
namespace test {
ReadingListLoadObserver::ReadingListLoadObserver(ReadingListModel* model)
: model_(model) {
model_->AddObserver(this);
}
ReadingListLoadObserver::~ReadingListLoadObserver() {
model_->RemoveObserver(this);
}
void ReadingListLoadObserver::Wait() {
if (model_->loaded())
return;
run_loop_.Run();
}
void ReadingListLoadObserver::ReadingListModelLoaded(
const ReadingListModel* model) {
run_loop_.Quit();
}
} // namespace test
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_READ_LATER_READ_LATER_TEST_UTILS_H_
#define CHROME_BROWSER_UI_READ_LATER_READ_LATER_TEST_UTILS_H_
#include "base/run_loop.h"
#include "components/reading_list/core/reading_list_model_observer.h"
class GURL;
class ReadingListModel;
namespace test {
// ReadingListLoadObserver is used to observe the ReadingListModel passed in the
// constructor for the ReadingListModelLoaded event.
class ReadingListLoadObserver : public ReadingListModelObserver {
public:
explicit ReadingListLoadObserver(ReadingListModel* model);
ReadingListLoadObserver(const ReadingListLoadObserver&) = delete;
ReadingListLoadObserver& operator=(const ReadingListLoadObserver&) = delete;
~ReadingListLoadObserver() override;
void Wait();
private:
// ReadingListModelObserver:
void ReadingListModelLoaded(const ReadingListModel* model) override;
void ReadingListModelBeganBatchUpdates(
const ReadingListModel* model) override {}
void ReadingListModelCompletedBatchUpdates(
const ReadingListModel* model) override {}
void ReadingListModelBeingShutdown(const ReadingListModel* model) override {}
void ReadingListModelBeingDeleted(const ReadingListModel* model) override {}
void ReadingListWillRemoveEntry(const ReadingListModel* model,
const GURL& url) override {}
void ReadingListWillMoveEntry(const ReadingListModel* model,
const GURL& url) override {}
void ReadingListDidMoveEntry(const ReadingListModel* model,
const GURL& url) override {}
void ReadingListWillAddEntry(const ReadingListModel* model,
const ReadingListEntry& entry) override {}
void ReadingListDidAddEntry(const ReadingListModel* model,
const GURL& url,
reading_list::EntrySource source) override {}
void ReadingListWillUpdateEntry(const ReadingListModel* model,
const GURL& url) override {}
void ReadingListDidApplyChanges(ReadingListModel* model) override {}
ReadingListModel* const model_;
base::RunLoop run_loop_;
};
} // namespace test
#endif // CHROME_BROWSER_UI_READ_LATER_READ_LATER_TEST_UTILS_H_
...@@ -31,6 +31,27 @@ ...@@ -31,6 +31,27 @@
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
namespace {
std::unique_ptr<KeyedService> BuildReadingListModel(
content::BrowserContext* context) {
Profile* const profile = Profile::FromBrowserContext(context);
syncer::OnceModelTypeStoreFactory store_factory =
ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory();
auto change_processor =
std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
syncer::READING_LIST,
base::BindRepeating(&syncer::ReportUnrecoverableError,
chrome::GetChannel()));
std::unique_ptr<ReadingListStore> store = std::make_unique<ReadingListStore>(
std::move(store_factory), std::move(change_processor));
return std::make_unique<ReadingListModelImpl>(
std::move(store), profile->GetPrefs(), base::DefaultClock::GetInstance());
}
} // namespace
// static // static
ReadingListModel* ReadingListModelFactory::GetForBrowserContext( ReadingListModel* ReadingListModelFactory::GetForBrowserContext(
content::BrowserContext* context) { content::BrowserContext* context) {
...@@ -43,6 +64,12 @@ ReadingListModelFactory* ReadingListModelFactory::GetInstance() { ...@@ -43,6 +64,12 @@ ReadingListModelFactory* ReadingListModelFactory::GetInstance() {
return base::Singleton<ReadingListModelFactory>::get(); return base::Singleton<ReadingListModelFactory>::get();
} }
// static
BrowserContextKeyedServiceFactory::TestingFactory
ReadingListModelFactory::GetDefaultFactoryForTesting() {
return base::BindRepeating(&BuildReadingListModel);
}
ReadingListModelFactory::ReadingListModelFactory() ReadingListModelFactory::ReadingListModelFactory()
: BrowserContextKeyedServiceFactory( : BrowserContextKeyedServiceFactory(
"ReadingListModel", "ReadingListModel",
...@@ -54,21 +81,7 @@ ReadingListModelFactory::~ReadingListModelFactory() = default; ...@@ -54,21 +81,7 @@ ReadingListModelFactory::~ReadingListModelFactory() = default;
KeyedService* ReadingListModelFactory::BuildServiceInstanceFor( KeyedService* ReadingListModelFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const { content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context); return BuildReadingListModel(context).release();
syncer::OnceModelTypeStoreFactory store_factory =
ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory();
auto change_processor =
std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
syncer::READING_LIST,
base::BindRepeating(&syncer::ReportUnrecoverableError,
chrome::GetChannel()));
std::unique_ptr<ReadingListStore> store = std::make_unique<ReadingListStore>(
std::move(store_factory), std::move(change_processor));
KeyedService* reading_list_model = new ReadingListModelImpl(
std::move(store), profile->GetPrefs(), base::DefaultClock::GetInstance());
return reading_list_model;
} }
void ReadingListModelFactory::RegisterProfilePrefs( void ReadingListModelFactory::RegisterProfilePrefs(
......
...@@ -27,6 +27,9 @@ class ReadingListModelFactory : public BrowserContextKeyedServiceFactory { ...@@ -27,6 +27,9 @@ class ReadingListModelFactory : public BrowserContextKeyedServiceFactory {
static ReadingListModelFactory* GetInstance(); static ReadingListModelFactory* GetInstance();
static BrowserContextKeyedServiceFactory::TestingFactory
GetDefaultFactoryForTesting();
private: private:
friend struct base::DefaultSingletonTraits<ReadingListModelFactory>; friend struct base::DefaultSingletonTraits<ReadingListModelFactory>;
......
...@@ -54,6 +54,7 @@ ReadLaterBubbleView::ReadLaterBubbleView(const Browser* browser, ...@@ -54,6 +54,7 @@ ReadLaterBubbleView::ReadLaterBubbleView(const Browser* browser,
// TODO(corising): Remove this and add function to calculate preferred size. // TODO(corising): Remove this and add function to calculate preferred size.
web_view_->SetPreferredSize(gfx::Size(300, 500)); web_view_->SetPreferredSize(gfx::Size(300, 500));
web_view_->LoadInitialURL(GURL(chrome::kChromeUIReadLaterURL));
} }
ReadLaterBubbleView::~ReadLaterBubbleView() = default; ReadLaterBubbleView::~ReadLaterBubbleView() = default;
......
...@@ -142,6 +142,7 @@ ...@@ -142,6 +142,7 @@
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h" #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/browser/ui/webui/page_not_available_for_guest/page_not_available_for_guest_ui.h" #include "chrome/browser/ui/webui/page_not_available_for_guest/page_not_available_for_guest_ui.h"
#include "chrome/browser/ui/webui/read_later/read_later_ui.h"
#include "chrome/browser/ui/webui/signin/inline_login_ui.h" #include "chrome/browser/ui/webui/signin/inline_login_ui.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h" #include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
#include "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h" #include "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h"
...@@ -551,6 +552,10 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, ...@@ -551,6 +552,10 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
return &NewWebUI<NewTabUI>; return &NewWebUI<NewTabUI>;
if (url.host_piece() == chrome::kChromeUINewTabPageHost) if (url.host_piece() == chrome::kChromeUINewTabPageHost)
return &NewWebUI<NewTabPageUI>; return &NewWebUI<NewTabPageUI>;
if (base::FeatureList::IsEnabled(features::kReadLater)) {
if (url.host_piece() == chrome::kChromeUIReadLaterHost)
return &NewWebUI<ReadLaterUI>;
}
// Settings are implemented with native UI elements on Android. // Settings are implemented with native UI elements on Android.
if (url.host_piece() == chrome::kChromeUISettingsHost) if (url.host_piece() == chrome::kChromeUISettingsHost)
return &NewWebUI<settings::SettingsUI>; return &NewWebUI<settings::SettingsUI>;
......
# Copyright 2020 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.
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojo_bindings") {
sources = [ "read_later.mojom" ]
public_deps = [
"//mojo/public/mojom/base",
"//url/mojom:url_mojom_gurl",
]
}
connily@chromium.org
corising@chromium.org
pbos@chromium.org
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
# COMPONENT: UI>Browser>ReadLater
// Copyright 2020 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.
module read_later.mojom;
import "mojo/public/mojom/base/string16.mojom";
import "url/mojom/url.mojom";
// Read later entries grouped by read/unread status.
struct ReadLaterEntriesByStatus {
array<ReadLaterEntry> unread_entries;
array<ReadLaterEntry> read_entries;
};
// Information about a read later entry.
struct ReadLaterEntry {
string title;
url.mojom.Url url;
mojo_base.mojom.String16 display_url;
int64 update_time;
mojo_base.mojom.String16 display_time_since_update;
};
// Used by the WebUI page to bootstrap bidirectional communication.
interface PageHandlerFactory {
// The WebUI calls this method when the page is first initialized.
CreatePageHandler(pending_remote<Page> page,
pending_receiver<PageHandler> handler);
};
// Browser-side handler for requests from WebUI page.
interface PageHandler {
// Get unread and read read later entries.
GetReadLaterEntries() => (ReadLaterEntriesByStatus entries);
// Open a saved read later entry.
OpenSavedEntry(url.mojom.Url url);
// Updates a read later entry's read status.
UpdateReadStatus(url.mojom.Url url, bool read);
// Removes a read later entry.
RemoveEntry(url.mojom.Url url);
};
// WebUI-side handler for requests from the browser.
interface Page {
};
// Copyright 2020 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 "chrome/browser/ui/webui/read_later/read_later_page_handler.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/strings/string16.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/read_later/reading_list_model_factory.h"
#include "components/reading_list/core/reading_list_entry.h"
#include "components/reading_list/core/reading_list_model.h"
#include "components/url_formatter/url_formatter.h"
#include "ui/base/l10n/time_format.h"
namespace {
// Sorter function that orders ReadingListEntries by their update time.
bool EntrySorter(const read_later::mojom::ReadLaterEntryPtr& rhs,
const read_later::mojom::ReadLaterEntryPtr& lhs) {
return rhs->update_time > lhs->update_time;
}
// Converts |time| to the number of microseconds since Jan 1st 1970.
// This matches the function used in the ReadingListEntry implementation.
int64_t TimeToUS(const base::Time& time) {
return (time - base::Time::UnixEpoch()).InMicroseconds();
}
} // namespace
ReadLaterPageHandler::ReadLaterPageHandler(
mojo::PendingReceiver<read_later::mojom::PageHandler> receiver,
mojo::PendingRemote<read_later::mojom::Page> page)
: receiver_(this, std::move(receiver)),
page_(std::move(page)),
browser_(chrome::FindLastActive()),
clock_(base::DefaultClock::GetInstance()) {
DCHECK(browser_);
reading_list_model_ =
ReadingListModelFactory::GetForBrowserContext(browser_->profile());
}
ReadLaterPageHandler::~ReadLaterPageHandler() = default;
void ReadLaterPageHandler::GetReadLaterEntries(
GetReadLaterEntriesCallback callback) {
auto entries = read_later::mojom::ReadLaterEntriesByStatus::New();
for (const auto& url : reading_list_model_->Keys()) {
const ReadingListEntry* entry = reading_list_model_->GetEntryByURL(url);
DCHECK(entry);
if (entry->IsRead()) {
entries->read_entries.push_back(GetEntryData(entry));
} else {
entries->unread_entries.push_back(GetEntryData(entry));
}
}
std::sort(entries->read_entries.begin(), entries->read_entries.end(),
EntrySorter);
std::sort(entries->unread_entries.begin(), entries->unread_entries.end(),
EntrySorter);
std::move(callback).Run(std::move(entries));
}
void ReadLaterPageHandler::OpenSavedEntry(const GURL& url) {
content::OpenURLParams params(url, content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_AUTO_BOOKMARK, false);
browser_->OpenURL(params);
reading_list_model_->SetReadStatus(url, true);
}
void ReadLaterPageHandler::UpdateReadStatus(const GURL& url, bool read) {
reading_list_model_->SetReadStatus(url, read);
}
void ReadLaterPageHandler::RemoveEntry(const GURL& url) {
reading_list_model_->RemoveEntryByURL(url);
}
read_later::mojom::ReadLaterEntryPtr ReadLaterPageHandler::GetEntryData(
const ReadingListEntry* entry) {
auto entry_data = read_later::mojom::ReadLaterEntry::New();
entry_data->title = entry->Title();
entry_data->url = entry->URL();
entry_data->display_url = url_formatter::FormatUrl(
entry->URL(),
url_formatter::kFormatUrlOmitDefaults |
url_formatter::kFormatUrlOmitHTTPS |
url_formatter::kFormatUrlOmitTrivialSubdomains |
url_formatter::kFormatUrlTrimAfterHost,
net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr);
entry_data->update_time = entry->UpdateTime();
entry_data->display_time_since_update =
GetTimeSinceLastUpdate(entry->UpdateTime());
return entry_data;
}
base::string16 ReadLaterPageHandler::GetTimeSinceLastUpdate(
int64_t last_update_time) {
const int64_t now = TimeToUS(clock_->Now());
if (last_update_time > now)
return base::string16();
const base::TimeDelta elapsed_time =
base::TimeDelta::FromMicroseconds(now - last_update_time);
return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
ui::TimeFormat::LENGTH_SHORT, elapsed_time);
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_WEBUI_READ_LATER_READ_LATER_PAGE_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_READ_LATER_READ_LATER_PAGE_HANDLER_H_
#include "chrome/browser/ui/webui/read_later/read_later.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "ui/webui/mojo_web_ui_controller.h"
namespace base {
class Clock;
}
class Browser;
class GURL;
class ReadingListEntry;
class ReadingListModel;
class ReadLaterPageHandler : public read_later::mojom::PageHandler {
public:
ReadLaterPageHandler(
mojo::PendingReceiver<read_later::mojom::PageHandler> receiver,
mojo::PendingRemote<read_later::mojom::Page> page);
ReadLaterPageHandler(const ReadLaterPageHandler&) = delete;
ReadLaterPageHandler& operator=(const ReadLaterPageHandler&) = delete;
~ReadLaterPageHandler() override;
// read_later::mojom::PageHandler:
void GetReadLaterEntries(GetReadLaterEntriesCallback callback) override;
void OpenSavedEntry(const GURL& url) override;
void UpdateReadStatus(const GURL& url, bool read) override;
void RemoveEntry(const GURL& url) override;
private:
// Gets the reading list entry data used for displaying to the user and
// triggering actions.
read_later::mojom::ReadLaterEntryPtr GetEntryData(
const ReadingListEntry* entry);
// Converts |last_update_time| from microseconds since epoch in Unix-like
// system (Jan 1, 1970), since this is how ReadingListEntry's |update_time| is
// stored, to a localized representation as a delay (e.g. "5 minutes ago").
base::string16 GetTimeSinceLastUpdate(int64_t last_update_time);
mojo::Receiver<read_later::mojom::PageHandler> receiver_;
mojo::Remote<read_later::mojom::Page> page_;
Browser* const browser_;
base::Clock* clock_;
ReadingListModel* reading_list_model_ = nullptr;
};
#endif // CHROME_BROWSER_UI_WEBUI_READ_LATER_READ_LATER_PAGE_HANDLER_H_
// Copyright 2020 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 "chrome/browser/ui/webui/read_later/read_later_page_handler.h"
#include <memory>
#include <string>
#include <utility>
#include "base/test/bind_test_util.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/read_later/read_later_test_utils.h"
#include "chrome/browser/ui/read_later/reading_list_model_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/test_browser_window.h"
#include "components/reading_list/core/reading_list_model.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
namespace {
constexpr char kTabUrl1[] = "http://foo/1";
constexpr char kTabUrl2[] = "http://foo/2";
constexpr char kTabUrl3[] = "http://foo/3";
constexpr char kTabUrl4[] = "http://foo/4";
constexpr char kTabName1[] = "Tab 1";
constexpr char kTabName2[] = "Tab 2";
constexpr char kTabName3[] = "Tab 3";
constexpr char kTabName4[] = "Tab 4";
class MockPage : public read_later::mojom::Page {
public:
MockPage() = default;
~MockPage() override = default;
mojo::PendingRemote<read_later::mojom::Page> BindAndGetRemote() {
DCHECK(!receiver_.is_bound());
return receiver_.BindNewPipeAndPassRemote();
}
mojo::Receiver<read_later::mojom::Page> receiver_{this};
};
void ExpectNewReadLaterEntry(const read_later::mojom::ReadLaterEntry* entry,
const GURL& url,
const std::string& title) {
EXPECT_EQ(title, entry->title);
EXPECT_EQ(url.spec(), entry->url.spec());
}
class TestReadLaterPageHandler : public ReadLaterPageHandler {
public:
explicit TestReadLaterPageHandler(
mojo::PendingRemote<read_later::mojom::Page> page)
: ReadLaterPageHandler(
mojo::PendingReceiver<read_later::mojom::PageHandler>(),
std::move(page)) {}
};
class TestReadLaterPageHandlerTest : public BrowserWithTestWindowTest {
public:
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
BrowserList::SetLastActive(browser());
handler_ =
std::make_unique<TestReadLaterPageHandler>(page_.BindAndGetRemote());
model_ =
ReadingListModelFactory::GetForBrowserContext(browser()->profile());
test::ReadingListLoadObserver(model_).Wait();
AddTabWithTitle(browser(), GURL(kTabUrl1), kTabName1);
AddTabWithTitle(browser(), GURL(kTabUrl2), kTabName2);
AddTabWithTitle(browser(), GURL(kTabUrl3), kTabName3);
AddTabWithTitle(browser(), GURL(kTabUrl4), kTabName4);
model()->AddEntry(GURL(kTabUrl1), kTabName1,
reading_list::EntrySource::ADDED_VIA_CURRENT_APP);
model()->AddEntry(GURL(kTabUrl3), kTabName3,
reading_list::EntrySource::ADDED_VIA_CURRENT_APP);
}
void TearDown() override {
browser()->tab_strip_model()->CloseAllTabs();
BrowserWithTestWindowTest::TearDown();
}
TestingProfile::TestingFactories GetTestingFactories() override {
return {{ReadingListModelFactory::GetInstance(),
ReadingListModelFactory::GetDefaultFactoryForTesting()}};
}
ReadingListModel* model() { return model_; }
TestReadLaterPageHandler* handler() { return handler_.get(); }
protected:
void AddTabWithTitle(Browser* browser,
const GURL url,
const std::string title) {
AddTab(browser, url);
NavigateAndCommitActiveTabWithTitle(browser, url,
base::ASCIIToUTF16(title));
}
private:
testing::StrictMock<MockPage> page_;
std::unique_ptr<TestReadLaterPageHandler> handler_;
ReadingListModel* model_;
};
TEST_F(TestReadLaterPageHandlerTest, GetReadLaterEntries) {
// Get Read later entries.
read_later::mojom::PageHandler::GetReadLaterEntriesCallback callback1 =
base::BindLambdaForTesting(
[&](read_later::mojom::ReadLaterEntriesByStatusPtr
entries_by_status) {
ASSERT_EQ(2u, entries_by_status->unread_entries.size());
ASSERT_EQ(0u, entries_by_status->read_entries.size());
// Verify the entries appear in order of last added to first.
auto* entry1 = entries_by_status->unread_entries[0].get();
ExpectNewReadLaterEntry(entry1, GURL(kTabUrl3), kTabName3);
auto* entry2 = entries_by_status->unread_entries[1].get();
ExpectNewReadLaterEntry(entry2, GURL(kTabUrl1), kTabName1);
});
handler()->GetReadLaterEntries(std::move(callback1));
}
TEST_F(TestReadLaterPageHandlerTest, OpenSavedEntry) {
// Check that OpenSavedEntry opens a new tab.
EXPECT_EQ(browser()->tab_strip_model()->count(), 4);
handler()->OpenSavedEntry(GURL(kTabUrl3));
EXPECT_EQ(browser()->tab_strip_model()->count(), 5);
// Get Read later entries.
read_later::mojom::PageHandler::GetReadLaterEntriesCallback callback1 =
base::BindLambdaForTesting(
[&](read_later::mojom::ReadLaterEntriesByStatusPtr
entries_by_status) {
ASSERT_EQ(1u, entries_by_status->unread_entries.size());
ASSERT_EQ(1u, entries_by_status->read_entries.size());
auto* entry1 = entries_by_status->unread_entries[0].get();
ExpectNewReadLaterEntry(entry1, GURL(kTabUrl1), kTabName1);
auto* entry2 = entries_by_status->read_entries[0].get();
ExpectNewReadLaterEntry(entry2, GURL(kTabUrl3), kTabName3);
});
handler()->GetReadLaterEntries(std::move(callback1));
}
TEST_F(TestReadLaterPageHandlerTest, UpdateReadStatus) {
handler()->UpdateReadStatus(GURL(kTabUrl3), true);
// Get Read later entries.
read_later::mojom::PageHandler::GetReadLaterEntriesCallback callback1 =
base::BindLambdaForTesting(
[&](read_later::mojom::ReadLaterEntriesByStatusPtr
entries_by_status) {
ASSERT_EQ(1u, entries_by_status->unread_entries.size());
ASSERT_EQ(1u, entries_by_status->read_entries.size());
auto* entry1 = entries_by_status->unread_entries[0].get();
ExpectNewReadLaterEntry(entry1, GURL(kTabUrl1), kTabName1);
auto* entry2 = entries_by_status->read_entries[0].get();
ExpectNewReadLaterEntry(entry2, GURL(kTabUrl3), kTabName3);
});
handler()->GetReadLaterEntries(std::move(callback1));
}
TEST_F(TestReadLaterPageHandlerTest, RemoveEntry) {
handler()->RemoveEntry(GURL(kTabUrl3));
// Get Read later entries.
read_later::mojom::PageHandler::GetReadLaterEntriesCallback callback1 =
base::BindLambdaForTesting(
[&](read_later::mojom::ReadLaterEntriesByStatusPtr
entries_by_status) {
ASSERT_EQ(1u, entries_by_status->unread_entries.size());
ASSERT_EQ(0u, entries_by_status->read_entries.size());
auto* entry1 = entries_by_status->unread_entries[0].get();
ExpectNewReadLaterEntry(entry1, GURL(kTabUrl1), kTabName1);
});
handler()->GetReadLaterEntries(std::move(callback1));
}
} // namespace
// Copyright 2020 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 "chrome/browser/ui/webui/read_later/read_later_ui.h"
#include <utility>
#include "chrome/browser/ui/webui/read_later/read_later_page_handler.h"
#include "chrome/common/webui_url_constants.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
ReadLaterUI::ReadLaterUI(content::WebUI* web_ui)
: ui::MojoWebUIController(web_ui) {
content::WebUIDataSource* source =
content::WebUIDataSource::Create(chrome::kChromeUIReadLaterHost);
content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
source);
}
ReadLaterUI::~ReadLaterUI() = default;
WEB_UI_CONTROLLER_TYPE_IMPL(ReadLaterUI)
void ReadLaterUI::BindInterface(
mojo::PendingReceiver<read_later::mojom::PageHandlerFactory> receiver) {
page_factory_receiver_.reset();
page_factory_receiver_.Bind(std::move(receiver));
}
void ReadLaterUI::CreatePageHandler(
mojo::PendingRemote<read_later::mojom::Page> page,
mojo::PendingReceiver<read_later::mojom::PageHandler> receiver) {
DCHECK(page);
page_handler_ = std::make_unique<ReadLaterPageHandler>(std::move(receiver),
std::move(page));
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_WEBUI_READ_LATER_READ_LATER_UI_H_
#define CHROME_BROWSER_UI_WEBUI_READ_LATER_READ_LATER_UI_H_
#include <memory>
#include "base/macros.h"
#include "chrome/browser/ui/webui/read_later/read_later.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/webui/mojo_web_ui_controller.h"
class ReadLaterPageHandler;
class ReadLaterUI : public ui::MojoWebUIController,
public read_later::mojom::PageHandlerFactory {
public:
explicit ReadLaterUI(content::WebUI* web_ui);
ReadLaterUI(const ReadLaterUI&) = delete;
ReadLaterUI& operator=(const ReadLaterUI&) = delete;
~ReadLaterUI() override;
// Instantiates the implementor of the mojom::PageHandlerFactory mojo
// interface passing the pending receiver that will be internally bound.
void BindInterface(
mojo::PendingReceiver<read_later::mojom::PageHandlerFactory> receiver);
private:
// read_later::mojom::PageHandlerFactory:
void CreatePageHandler(
mojo::PendingRemote<read_later::mojom::Page> page,
mojo::PendingReceiver<read_later::mojom::PageHandler> receiver) override;
std::unique_ptr<ReadLaterPageHandler> page_handler_;
mojo::Receiver<read_later::mojom::PageHandlerFactory> page_factory_receiver_{
this};
WEB_UI_CONTROLLER_TYPE_DECL();
};
#endif // CHROME_BROWSER_UI_WEBUI_READ_LATER_READ_LATER_UI_H_
...@@ -199,6 +199,8 @@ const char kChromeUISnippetsInternalsHost[] = "snippets-internals"; ...@@ -199,6 +199,8 @@ const char kChromeUISnippetsInternalsHost[] = "snippets-internals";
const char kChromeUIWebApksHost[] = "webapks"; const char kChromeUIWebApksHost[] = "webapks";
#else #else
const char kChromeUINearbyInternalsHost[] = "nearby-internals"; const char kChromeUINearbyInternalsHost[] = "nearby-internals";
const char kChromeUIReadLaterHost[] = "read-later";
const char kChromeUIReadLaterURL[] = "chrome://read-later/";
#endif #endif
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
......
...@@ -197,6 +197,8 @@ extern const char kChromeUISnippetsInternalsHost[]; ...@@ -197,6 +197,8 @@ extern const char kChromeUISnippetsInternalsHost[];
extern const char kChromeUIWebApksHost[]; extern const char kChromeUIWebApksHost[];
#else #else
extern const char kChromeUINearbyInternalsHost[]; extern const char kChromeUINearbyInternalsHost[];
extern const char kChromeUIReadLaterHost[];
extern const char kChromeUIReadLaterURL[];
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID)
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
......
...@@ -4319,6 +4319,8 @@ test("unit_tests") { ...@@ -4319,6 +4319,8 @@ test("unit_tests") {
"../browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc", "../browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc",
"../browser/ui/passwords/credential_manager_dialog_controller_impl_unittest.cc", "../browser/ui/passwords/credential_manager_dialog_controller_impl_unittest.cc",
"../browser/ui/qrcode_generator/qrcode_generator_bubble_controller_unittest.cc", "../browser/ui/qrcode_generator/qrcode_generator_bubble_controller_unittest.cc",
"../browser/ui/read_later/read_later_test_utils.cc",
"../browser/ui/read_later/read_later_test_utils.h",
"../browser/ui/recently_audible_helper_unittest.cc", "../browser/ui/recently_audible_helper_unittest.cc",
"../browser/ui/search/ntp_user_data_logger_unittest.cc", "../browser/ui/search/ntp_user_data_logger_unittest.cc",
"../browser/ui/search/omnibox_mojo_utils_unittest.cc", "../browser/ui/search/omnibox_mojo_utils_unittest.cc",
...@@ -4368,6 +4370,7 @@ test("unit_tests") { ...@@ -4368,6 +4370,7 @@ test("unit_tests") {
"../browser/ui/webui/management_ui_handler_unittest.cc", "../browser/ui/webui/management_ui_handler_unittest.cc",
"../browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc", "../browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc",
"../browser/ui/webui/new_tab_page/promo_browser_command/promo_browser_command_handler_unittest.cc", "../browser/ui/webui/new_tab_page/promo_browser_command/promo_browser_command_handler_unittest.cc",
"../browser/ui/webui/read_later/read_later_page_handler_unittest.cc",
"../browser/ui/webui/sanitized_image_source_unittest.cc", "../browser/ui/webui/sanitized_image_source_unittest.cc",
"../browser/ui/webui/settings/downloads_handler_unittest.cc", "../browser/ui/webui/settings/downloads_handler_unittest.cc",
"../browser/ui/webui/settings/hats_handler_unittest.cc", "../browser/ui/webui/settings/hats_handler_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment