Commit a49acec4 authored by Kristi Park's avatar Kristi Park Committed by Commit Bot

[NTP] Implement drag & drop for reordering custom links

Allow users to reorder their custom links with drag and drop.
Screencast: https://screencast.googleplex.com/cast/NDkxMzY1Mzc2OTgzMDQwMHxmY2JhMTNkYy01NQ

Bug: 851335
Change-Id: I8e77fbdd7c10ec95f60dc5a41e45ae06f7a62543
Reviewed-on: https://chromium-review.googlesource.com/c/1309228
Commit-Queue: Kristi Park <kristipark@chromium.org>
Reviewed-by: default avatarRamya Nagarajan <ramyan@chromium.org>
Reviewed-by: default avatarMustafa Emre Acer <meacer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606189}
parent 81758e5d
...@@ -392,6 +392,17 @@ html[dir=rtl] .mv-favicon { ...@@ -392,6 +392,17 @@ html[dir=rtl] .mv-favicon {
width: var(--md-tile-width); width: var(--md-tile-width);
} }
.md-tile-container.reorder .md-tile {
background-color: white;
box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3),
0 4px 8px 3px rgba(60, 64, 67, 0.15);
transition-duration: 200ms;
}
.md-tile-container.reorder .md-tile .md-tile-inner {
z-index: unset;
}
.md-tile { .md-tile {
cursor: pointer; cursor: pointer;
padding: var(--md-tile-padding-vertical) var(--md-tile-padding-horizontal); padding: var(--md-tile-padding-vertical) var(--md-tile-padding-horizontal);
...@@ -406,20 +417,20 @@ body.using-theme .md-tile { ...@@ -406,20 +417,20 @@ body.using-theme .md-tile {
display: none; display: none;
} }
.md-tile-container:hover { body:not(.reordering) .md-tile-container:hover {
background-color: rgba(33, 32, 36, 0.06); background-color: rgba(33, 32, 36, 0.06);
} }
body.dark-theme .md-tile-container:hover { body.dark-theme:not(.reordering) .md-tile-container:hover {
background-color: rgba(255, 255, 255, 0.1); background-color: rgba(255, 255, 255, 0.1);
} }
.md-tile-container:hover .md-menu { body:not(.reordering) .md-tile-container:hover .md-menu {
opacity: 1; opacity: 1;
transition-delay: 500ms; transition-delay: 500ms;
} }
body.dark-theme .md-tile-container:active + .md-menu::after { body.dark-theme:not(.reordering) .md-tile-container:active + .md-menu::after {
background-color: rgb(189, 193, 198); background-color: rgb(189, 193, 198);
transition-delay: 0ms; transition-delay: 0ms;
} }
...@@ -538,8 +549,8 @@ body.using-theme .md-title { ...@@ -538,8 +549,8 @@ body.using-theme .md-title {
width: var(--md-menu-size); width: var(--md-menu-size);
} }
.md-menu:active, body:not(.reordering) .md-menu:active,
.md-menu:focus:not(.mouse-navigation) { body:not(.reordering) .md-menu:focus:not(.mouse-navigation) {
opacity: 1; opacity: 1;
} }
...@@ -582,12 +593,12 @@ html[dir=rtl] .md-menu { ...@@ -582,12 +593,12 @@ html[dir=rtl] .md-menu {
right: auto; right: auto;
} }
.md-menu:hover::after, body:not(.reordering) .md-menu:hover::after,
.md-menu:focus::after { body:not(.reordering) .md-menu:focus::after {
background-color: rgba(33, 32, 36, 0.71); background-color: rgba(33, 32, 36, 0.71);
} }
body.dark-theme .md-menu:hover::after, body.dark-theme:not(.reordering) .md-menu:hover::after,
body.dark-theme .md-menu:focus::after { body.dark-theme:not(.reordering) .md-menu:focus::after {
background-color: rgb(218, 220, 224); background-color: rgb(218, 220, 224);
} }
...@@ -7,6 +7,17 @@ ...@@ -7,6 +7,17 @@
'use strict'; 'use strict';
/**
* Alias for document.getElementById.
* @param {string} id The ID of the element to find.
* @return {HTMLElement} The found element or null if not found.
*/
function $(id) {
// eslint-disable-next-line no-restricted-properties
return document.getElementById(id);
}
/** /**
* Enum for key codes. * Enum for key codes.
* @enum {int} * @enum {int}
...@@ -42,6 +53,8 @@ const IDS = { ...@@ -42,6 +53,8 @@ const IDS = {
*/ */
const CLASSES = { const CLASSES = {
FAILED_FAVICON: 'failed-favicon', // Applied when the favicon fails to load. FAILED_FAVICON: 'failed-favicon', // Applied when the favicon fails to load.
REORDER: 'reorder', // Applied to the tile being moved while reordering.
REORDERING: 'reordering', // Applied while we are reordering.
// Material Design classes. // Material Design classes.
MATERIAL_DESIGN: 'md', // Applies Material Design styles to the page. MATERIAL_DESIGN: 'md', // Applies Material Design styles to the page.
MD_EMPTY_TILE: 'md-empty-tile', MD_EMPTY_TILE: 'md-empty-tile',
...@@ -112,6 +125,13 @@ var TileVisualType = { ...@@ -112,6 +125,13 @@ var TileVisualType = {
const RESIZE_TIMEOUT_DELAY = 66; const RESIZE_TIMEOUT_DELAY = 66;
/**
* Timeout delay in ms before starting the reorder flow.
* @const {number}
*/
const REORDER_TIMEOUT_DELAY = 1000;
/** /**
* Maximum number of tiles if custom links is enabled. * Maximum number of tiles if custom links is enabled.
* @const {number} * @const {number}
...@@ -198,6 +218,21 @@ let maxNumTiles = 8; ...@@ -198,6 +218,21 @@ let maxNumTiles = 8;
var queryArgs = {}; var queryArgs = {};
/**
* True if we are currently reordering the tiles.
* @type {boolean}
*/
let reordering = false;
/**
* The tile that is being moved during the reorder flow. Null if we are
* currently not reordering.
* @type {?Element}
*/
let elementToReorder = null;
/** /**
* True if Material Design styles should be applied. * True if Material Design styles should be applied.
* @type {boolean} * @type {boolean}
...@@ -558,6 +593,96 @@ function editCustomLink(tid) { ...@@ -558,6 +593,96 @@ function editCustomLink(tid) {
} }
/**
* Starts the reorder flow. Updates the visual style of the held tile to
* indicate that it is being moved.
* @param {!Element} tile Tile that is being moved.
*/
function startReorder(tile) {
reordering = true;
elementToReorder = tile;
tile.classList.add(CLASSES.REORDER);
// Disable other hover/active styling for all tiles.
document.body.classList.add(CLASSES.REORDERING);
document.addEventListener('dragend', () => {
stopReorder(tile);
}, {once: true});
}
/**
* Stops the reorder flow. Resets the held tile's visual style and tells the
* EmbeddedSearchAPI that a tile has been moved.
* @param {!Element} tile Tile that has been moved.
*/
function stopReorder(tile) {
reordering = false;
elementToReorder = null;
tile.classList.remove(CLASSES.REORDER);
document.body.classList.remove(CLASSES.REORDERING);
// Update |data-pos| for all tiles and notify EmbeddedSearchAPI that the tile
// has been moved.
const allTiles = document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE);
for (let i = 0; i < allTiles.length; i++)
allTiles[i].setAttribute('data-pos', i);
chrome.embeddedSearch.newTabPage.reorderCustomLink(
Number(tile.firstChild.getAttribute('data-tid')),
Number(tile.firstChild.getAttribute('data-pos')));
}
/**
* Sets up event listeners necessary for tile reordering.
* @param {!Element} tile Tile on which to set the event listeners.
*/
function setupReorder(tile) {
// Starts the reorder flow after the user has held the mouse button down for
// |REORDER_TIMEOUT_DELAY|.
tile.addEventListener('mousedown', (event) => {
if (event.button == 0 /* LEFT CLICK */) {
let timeout = -1;
// Cancel the timeout if the user drags the mouse off the tile and
// releases.
let dragend = document.addEventListener('dragend', () => {
window.clearTimeout(timeout);
}, {once: true});
// Wait for |REORDER_TIMEOUT_DELAY| before starting the reorder flow.
timeout = window.setTimeout(() => {
if (!reordering)
startReorder(tile);
document.removeEventListener('dragend', dragend);
}, REORDER_TIMEOUT_DELAY);
}
});
tile.addEventListener('dragover', (event) => {
// Only executed when the reorder flow is ongoing. Inserts the tile that is
// being moved before/after this |tile| according to order in the list.
if (reordering && elementToReorder && elementToReorder != tile) {
// Determine which side to insert the element on:
// - If the held tile comes after the current tile, insert behind the
// current tile.
// - If the held tile comes before the current tile, insert in front of
// the current tile.
let insertBefore; // Element to insert the held tile behind.
if (tile.compareDocumentPosition(elementToReorder) &
Node.DOCUMENT_POSITION_FOLLOWING) {
insertBefore = tile;
} else {
insertBefore = tile.nextSibling;
}
$('mv-tiles').insertBefore(elementToReorder, insertBefore);
}
});
}
/** /**
* Returns whether the given URL has a known, safe scheme. * Returns whether the given URL has a known, safe scheme.
* @param {string} url URL to check. * @param {string} url URL to check.
...@@ -935,6 +1060,12 @@ function renderMaterialDesignTile(data) { ...@@ -935,6 +1060,12 @@ function renderMaterialDesignTile(data) {
mdTileContainer.appendChild(mdMenu); mdTileContainer.appendChild(mdMenu);
} }
// Enable reordering.
if (isCustomLinksEnabled && !data.isAddButton) {
mdTileContainer.draggable = 'true';
setupReorder(mdTileContainer);
}
return mdTileContainer; return mdTileContainer;
} }
......
...@@ -316,6 +316,12 @@ bool InstantService::UpdateCustomLink(const GURL& url, ...@@ -316,6 +316,12 @@ bool InstantService::UpdateCustomLink(const GURL& url,
return false; return false;
} }
bool InstantService::ReorderCustomLink(const GURL& url, int new_pos) {
if (most_visited_sites_)
return most_visited_sites_->ReorderCustomLink(url, new_pos);
return false;
}
bool InstantService::DeleteCustomLink(const GURL& url) { bool InstantService::DeleteCustomLink(const GURL& url) {
if (most_visited_sites_) if (most_visited_sites_)
return most_visited_sites_->DeleteCustomLink(url); return most_visited_sites_->DeleteCustomLink(url);
......
...@@ -88,6 +88,8 @@ class InstantService : public KeyedService, ...@@ -88,6 +88,8 @@ class InstantService : public KeyedService,
bool UpdateCustomLink(const GURL& url, bool UpdateCustomLink(const GURL& url,
const GURL& new_url, const GURL& new_url,
const std::string& new_title); const std::string& new_title);
// Invoked when the Instant page wants to reorder a custom link.
bool ReorderCustomLink(const GURL& url, int new_pos);
// Invoked when the Instant page wants to delete a custom link. // Invoked when the Instant page wants to delete a custom link.
bool DeleteCustomLink(const GURL& url); bool DeleteCustomLink(const GURL& url);
// Invoked when the Instant page wants to undo the previous custom link // Invoked when the Instant page wants to undo the previous custom link
......
...@@ -644,7 +644,7 @@ IN_PROC_BROWSER_TEST_F(LocalNTPCustomLinksTest, ...@@ -644,7 +644,7 @@ IN_PROC_BROWSER_TEST_F(LocalNTPCustomLinksTest,
++i) { ++i) {
std::string rid = std::to_string(i + 100); std::string rid = std::to_string(i + 100);
std::string url = "https://" + rid + ".com"; std::string url = "https://" + rid + ".com";
std::string title = "url for" + rid; std::string title = "url for " + rid;
// Add most visited tiles via the EmbeddedSearch API. rid = -1 means add new // Add most visited tiles via the EmbeddedSearch API. rid = -1 means add new
// most visited tile. // most visited tile.
EXPECT_TRUE(content::ExecuteScript( EXPECT_TRUE(content::ExecuteScript(
...@@ -668,6 +668,86 @@ IN_PROC_BROWSER_TEST_F(LocalNTPCustomLinksTest, ...@@ -668,6 +668,86 @@ IN_PROC_BROWSER_TEST_F(LocalNTPCustomLinksTest,
EXPECT_TRUE(no_add_button); EXPECT_TRUE(no_add_button);
} }
IN_PROC_BROWSER_TEST_F(LocalNTPCustomLinksTest, Reorder) {
content::WebContents* active_tab =
local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
TestMostVisitedObserver observer(
InstantServiceFactory::GetForProfile(browser()->profile()));
local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
observer.WaitForNumberOfItems(kDefaultMostVisitedItemCount);
// Fill tiles up to the maximum count.
content::RenderFrameHost* iframe = GetMostVisitedIframe(active_tab);
for (int i = kDefaultMostVisitedItemCount; i < kDefaultCustomLinkMaxCount;
++i) {
std::string rid = std::to_string(i + 100);
std::string url = "https://" + rid + ".com";
std::string title = "url for " + rid;
ASSERT_TRUE(content::ExecuteScript(
iframe,
"window.chrome.embeddedSearch.newTabPage.updateCustomLink(-1, '" + url +
"', '" + title + "')"));
}
// Confirm that there are max number of custom link tiles.
observer.WaitForNumberOfItems(kDefaultCustomLinkMaxCount);
// Open a new tab to get the updated links.
active_tab = local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
iframe = GetMostVisitedIframe(active_tab);
// Get the title of the tile at index 1.
std::string title;
ASSERT_TRUE(instant_test_utils::GetStringFromJS(
iframe, "document.querySelectorAll('.md-tile .md-title')[1].innerText",
&title));
// Move the tile to the front.
std::string tid;
ASSERT_TRUE(instant_test_utils::GetStringFromJS(
iframe,
"document.querySelectorAll('.md-tile')[1].getAttribute('data-tid')",
&tid));
EXPECT_TRUE(content::ExecuteScript(
iframe, "window.chrome.embeddedSearch.newTabPage.reorderCustomLink(" +
tid + ", 0)"));
// Open a new tab to get the updated links.
active_tab = local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
iframe = GetMostVisitedIframe(active_tab);
// Check that the first tile is the tile that was moved.
std::string new_title;
ASSERT_TRUE(instant_test_utils::GetStringFromJS(
iframe, "document.querySelectorAll('.md-tile .md-title')[0].innerText",
&new_title));
EXPECT_EQ(new_title, title);
// Move the tile again to the end.
std::string end_index = std::to_string(kDefaultCustomLinkMaxCount - 1);
ASSERT_TRUE(instant_test_utils::GetStringFromJS(
iframe,
"document.querySelectorAll('.md-tile')[0].getAttribute('data-tid')",
&tid));
EXPECT_TRUE(content::ExecuteScript(
iframe, "window.chrome.embeddedSearch.newTabPage.reorderCustomLink(" +
tid + ", " + end_index + ")"));
// Open a new tab to get the updated links.
active_tab = local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
iframe = GetMostVisitedIframe(active_tab);
// Check that the last tile is the tile that was moved.
new_title = std::string();
ASSERT_TRUE(instant_test_utils::GetStringFromJS(
iframe,
"document.querySelectorAll('.md-tile .md-title')[" + end_index +
"].innerText",
&new_title));
EXPECT_EQ(new_title, title);
}
// A minimal implementation of an interstitial page. // A minimal implementation of an interstitial page.
class TestInterstitialPageDelegate : public content::InterstitialPageDelegate { class TestInterstitialPageDelegate : public content::InterstitialPageDelegate {
public: public:
......
...@@ -215,6 +215,18 @@ void SearchIPCRouter::UpdateCustomLink(int page_seq_no, ...@@ -215,6 +215,18 @@ void SearchIPCRouter::UpdateCustomLink(int page_seq_no,
std::move(callback).Run(result); std::move(callback).Run(result);
} }
void SearchIPCRouter::ReorderCustomLink(int page_seq_no,
const GURL& url,
int new_pos) {
if (page_seq_no != commit_counter_)
return;
if (!policy_->ShouldProcessReorderCustomLink())
return;
delegate_->OnReorderCustomLink(url, new_pos);
}
void SearchIPCRouter::DeleteCustomLink(int page_seq_no, void SearchIPCRouter::DeleteCustomLink(int page_seq_no,
const GURL& url, const GURL& url,
DeleteCustomLinkCallback callback) { DeleteCustomLinkCallback callback) {
......
...@@ -62,6 +62,9 @@ class SearchIPCRouter : public content::WebContentsObserver, ...@@ -62,6 +62,9 @@ class SearchIPCRouter : public content::WebContentsObserver,
const GURL& new_url, const GURL& new_url,
const std::string& new_title) = 0; const std::string& new_title) = 0;
// Called when the EmbeddedSearch wants to reorder a custom link.
virtual bool OnReorderCustomLink(const GURL& url, int new_pos) = 0;
// Called when the EmbeddedSearch wants to delete a custom link. // Called when the EmbeddedSearch wants to delete a custom link.
virtual bool OnDeleteCustomLink(const GURL& url) = 0; virtual bool OnDeleteCustomLink(const GURL& url) = 0;
...@@ -132,6 +135,7 @@ class SearchIPCRouter : public content::WebContentsObserver, ...@@ -132,6 +135,7 @@ class SearchIPCRouter : public content::WebContentsObserver,
virtual bool ShouldProcessUndoAllMostVisitedDeletions() = 0; virtual bool ShouldProcessUndoAllMostVisitedDeletions() = 0;
virtual bool ShouldProcessAddCustomLink() = 0; virtual bool ShouldProcessAddCustomLink() = 0;
virtual bool ShouldProcessUpdateCustomLink() = 0; virtual bool ShouldProcessUpdateCustomLink() = 0;
virtual bool ShouldProcessReorderCustomLink() = 0;
virtual bool ShouldProcessDeleteCustomLink() = 0; virtual bool ShouldProcessDeleteCustomLink() = 0;
virtual bool ShouldProcessUndoCustomLinkAction() = 0; virtual bool ShouldProcessUndoCustomLinkAction() = 0;
virtual bool ShouldProcessResetCustomLinks() = 0; virtual bool ShouldProcessResetCustomLinks() = 0;
...@@ -203,6 +207,9 @@ class SearchIPCRouter : public content::WebContentsObserver, ...@@ -203,6 +207,9 @@ class SearchIPCRouter : public content::WebContentsObserver,
const GURL& new_url, const GURL& new_url,
const std::string& new_title, const std::string& new_title,
UpdateCustomLinkCallback callback) override; UpdateCustomLinkCallback callback) override;
void ReorderCustomLink(int page_seq_no,
const GURL& url,
int new_pos) override;
void DeleteCustomLink(int page_seq_no, void DeleteCustomLink(int page_seq_no,
const GURL& url, const GURL& url,
DeleteCustomLinkCallback callback) override; DeleteCustomLinkCallback callback) override;
......
...@@ -46,6 +46,10 @@ bool SearchIPCRouterPolicyImpl::ShouldProcessUpdateCustomLink() { ...@@ -46,6 +46,10 @@ bool SearchIPCRouterPolicyImpl::ShouldProcessUpdateCustomLink() {
return !is_incognito_ && search::IsInstantNTP(web_contents_); return !is_incognito_ && search::IsInstantNTP(web_contents_);
} }
bool SearchIPCRouterPolicyImpl::ShouldProcessReorderCustomLink() {
return !is_incognito_ && search::IsInstantNTP(web_contents_);
}
bool SearchIPCRouterPolicyImpl::ShouldProcessDeleteCustomLink() { bool SearchIPCRouterPolicyImpl::ShouldProcessDeleteCustomLink() {
return !is_incognito_ && search::IsInstantNTP(web_contents_); return !is_incognito_ && search::IsInstantNTP(web_contents_);
} }
......
...@@ -33,6 +33,7 @@ class SearchIPCRouterPolicyImpl : public SearchIPCRouter::Policy { ...@@ -33,6 +33,7 @@ class SearchIPCRouterPolicyImpl : public SearchIPCRouter::Policy {
bool ShouldProcessUndoAllMostVisitedDeletions() override; bool ShouldProcessUndoAllMostVisitedDeletions() override;
bool ShouldProcessAddCustomLink() override; bool ShouldProcessAddCustomLink() override;
bool ShouldProcessUpdateCustomLink() override; bool ShouldProcessUpdateCustomLink() override;
bool ShouldProcessReorderCustomLink() override;
bool ShouldProcessDeleteCustomLink() override; bool ShouldProcessDeleteCustomLink() override;
bool ShouldProcessUndoCustomLinkAction() override; bool ShouldProcessUndoCustomLinkAction() override;
bool ShouldProcessResetCustomLinks() override; bool ShouldProcessResetCustomLinks() override;
......
...@@ -67,6 +67,7 @@ class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate { ...@@ -67,6 +67,7 @@ class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate {
bool(const GURL& url, bool(const GURL& url,
const GURL& new_url, const GURL& new_url,
const std::string& new_title)); const std::string& new_title));
MOCK_METHOD2(OnReorderCustomLink, bool(const GURL& url, int new_pos));
MOCK_METHOD1(OnDeleteCustomLink, bool(const GURL& url)); MOCK_METHOD1(OnDeleteCustomLink, bool(const GURL& url));
MOCK_METHOD0(OnUndoCustomLinkAction, void()); MOCK_METHOD0(OnUndoCustomLinkAction, void());
MOCK_METHOD0(OnResetCustomLinks, void()); MOCK_METHOD0(OnResetCustomLinks, void());
...@@ -98,6 +99,7 @@ class MockSearchIPCRouterPolicy : public SearchIPCRouter::Policy { ...@@ -98,6 +99,7 @@ class MockSearchIPCRouterPolicy : public SearchIPCRouter::Policy {
MOCK_METHOD0(ShouldProcessUndoAllMostVisitedDeletions, bool()); MOCK_METHOD0(ShouldProcessUndoAllMostVisitedDeletions, bool());
MOCK_METHOD0(ShouldProcessAddCustomLink, bool()); MOCK_METHOD0(ShouldProcessAddCustomLink, bool());
MOCK_METHOD0(ShouldProcessUpdateCustomLink, bool()); MOCK_METHOD0(ShouldProcessUpdateCustomLink, bool());
MOCK_METHOD0(ShouldProcessReorderCustomLink, bool());
MOCK_METHOD0(ShouldProcessDeleteCustomLink, bool()); MOCK_METHOD0(ShouldProcessDeleteCustomLink, bool());
MOCK_METHOD0(ShouldProcessUndoCustomLinkAction, bool()); MOCK_METHOD0(ShouldProcessUndoCustomLinkAction, bool());
MOCK_METHOD0(ShouldProcessResetCustomLinks, bool()); MOCK_METHOD0(ShouldProcessResetCustomLinks, bool());
...@@ -552,6 +554,38 @@ TEST_F(SearchIPCRouterTest, IgnoreUpdateCustomLinkMsg) { ...@@ -552,6 +554,38 @@ TEST_F(SearchIPCRouterTest, IgnoreUpdateCustomLinkMsg) {
new_url, new_title, callback.Get()); new_url, new_title, callback.Get());
} }
TEST_F(SearchIPCRouterTest, ProcessReorderCustomLinkMsg) {
NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
SetupMockDelegateAndPolicy();
MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
GURL item_url("www.foo.com");
int new_pos = 1;
EXPECT_CALL(*mock_delegate(), OnReorderCustomLink(item_url, new_pos))
.Times(1);
EXPECT_CALL(*policy, ShouldProcessReorderCustomLink())
.Times(1)
.WillOnce(Return(true));
GetSearchIPCRouter().ReorderCustomLink(GetSearchIPCRouterSeqNo(), item_url,
new_pos);
}
TEST_F(SearchIPCRouterTest, IgnoreReorderCustomLinkMsg) {
NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
SetupMockDelegateAndPolicy();
MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
GURL item_url("www.foo.com");
int new_pos = 1;
EXPECT_CALL(*mock_delegate(), OnReorderCustomLink(item_url, new_pos))
.Times(0);
EXPECT_CALL(*policy, ShouldProcessReorderCustomLink())
.Times(1)
.WillOnce(Return(false));
GetSearchIPCRouter().ReorderCustomLink(GetSearchIPCRouterSeqNo(), item_url,
new_pos);
}
TEST_F(SearchIPCRouterTest, ProcessDeleteCustomLinkMsg) { TEST_F(SearchIPCRouterTest, ProcessDeleteCustomLinkMsg) {
NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar")); NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
SetupMockDelegateAndPolicy(); SetupMockDelegateAndPolicy();
......
...@@ -303,6 +303,13 @@ bool SearchTabHelper::OnUpdateCustomLink(const GURL& url, ...@@ -303,6 +303,13 @@ bool SearchTabHelper::OnUpdateCustomLink(const GURL& url,
return false; return false;
} }
bool SearchTabHelper::OnReorderCustomLink(const GURL& url, int new_pos) {
DCHECK(!url.is_empty());
if (instant_service_)
return instant_service_->ReorderCustomLink(url, new_pos);
return false;
}
bool SearchTabHelper::OnDeleteCustomLink(const GURL& url) { bool SearchTabHelper::OnDeleteCustomLink(const GURL& url) {
DCHECK(!url.is_empty()); DCHECK(!url.is_empty());
if (instant_service_) if (instant_service_)
......
...@@ -104,6 +104,7 @@ class SearchTabHelper : public content::WebContentsObserver, ...@@ -104,6 +104,7 @@ class SearchTabHelper : public content::WebContentsObserver,
bool OnUpdateCustomLink(const GURL& url, bool OnUpdateCustomLink(const GURL& url,
const GURL& new_url, const GURL& new_url,
const std::string& new_title) override; const std::string& new_title) override;
bool OnReorderCustomLink(const GURL& url, int new_pos) override;
bool OnDeleteCustomLink(const GURL& url) override; bool OnDeleteCustomLink(const GURL& url) override;
void OnUndoCustomLinkAction() override; void OnUndoCustomLinkAction() override;
void OnResetCustomLinks() override; void OnResetCustomLinks() override;
......
...@@ -63,6 +63,7 @@ class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate { ...@@ -63,6 +63,7 @@ class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate {
bool(const GURL& url, bool(const GURL& url,
const GURL& new_url, const GURL& new_url,
const std::string& new_title)); const std::string& new_title));
MOCK_METHOD2(OnReorderCustomLink, bool(const GURL& url, int new_pos));
MOCK_METHOD1(OnDeleteCustomLink, bool(const GURL& url)); MOCK_METHOD1(OnDeleteCustomLink, bool(const GURL& url));
MOCK_METHOD0(OnUndoCustomLinkAction, void()); MOCK_METHOD0(OnUndoCustomLinkAction, void());
MOCK_METHOD0(OnResetCustomLinks, void()); MOCK_METHOD0(OnResetCustomLinks, void());
......
...@@ -55,6 +55,9 @@ interface EmbeddedSearch { ...@@ -55,6 +55,9 @@ interface EmbeddedSearch {
url.mojom.Url new_url, url.mojom.Url new_url,
string new_title) => (bool success); string new_title) => (bool success);
// Tells InstantExtended to reorder a custom link.
ReorderCustomLink(int32 page_seq_no, url.mojom.Url url, int32 new_pos);
// Tells InstantExtended to delete a custom link. Returns true if successful. // Tells InstantExtended to delete a custom link. Returns true if successful.
DeleteCustomLink(int32 page_seq_no, url.mojom.Url url) => (bool success); DeleteCustomLink(int32 page_seq_no, url.mojom.Url url) => (bool success);
......
...@@ -369,6 +369,13 @@ void SearchBox::UpdateCustomLink(InstantRestrictedID link_id, ...@@ -369,6 +369,13 @@ void SearchBox::UpdateCustomLink(InstantRestrictedID link_id,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void SearchBox::ReorderCustomLink(InstantRestrictedID link_id, int new_pos) {
GURL url = GetURLForMostVisitedItem(link_id);
if (!url.is_valid())
return;
embedded_search_service_->ReorderCustomLink(page_seq_no_, url, new_pos);
}
void SearchBox::DeleteCustomLink(InstantRestrictedID most_visited_item_id) { void SearchBox::DeleteCustomLink(InstantRestrictedID most_visited_item_id) {
GURL url = GetURLForMostVisitedItem(most_visited_item_id); GURL url = GetURLForMostVisitedItem(most_visited_item_id);
if (!url.is_valid()) { if (!url.is_valid()) {
......
...@@ -130,6 +130,9 @@ class SearchBox : public content::RenderFrameObserver, ...@@ -130,6 +130,9 @@ class SearchBox : public content::RenderFrameObserver,
const GURL& new_url, const GURL& new_url,
const std::string& new_title); const std::string& new_title);
// Sends ReorderCustomLink to the browser.
void ReorderCustomLink(InstantRestrictedID link_id, int new_pos);
// Sends DeleteCustomLink to the browser. // Sends DeleteCustomLink to the browser.
void DeleteCustomLink(InstantRestrictedID most_visited_item_id); void DeleteCustomLink(InstantRestrictedID most_visited_item_id);
......
...@@ -627,6 +627,7 @@ class NewTabPageBindings : public gin::Wrappable<NewTabPageBindings> { ...@@ -627,6 +627,7 @@ class NewTabPageBindings : public gin::Wrappable<NewTabPageBindings> {
static void UpdateCustomLink(int rid, static void UpdateCustomLink(int rid,
const std::string& url, const std::string& url,
const std::string& title); const std::string& title);
static void ReorderCustomLink(int rid, int new_pos);
static void UndoCustomLinkAction(); static void UndoCustomLinkAction();
static void ResetCustomLinks(); static void ResetCustomLinks();
static std::string FixupAndValidateUrl(const std::string& url); static std::string FixupAndValidateUrl(const std::string& url);
...@@ -683,6 +684,7 @@ gin::ObjectTemplateBuilder NewTabPageBindings::GetObjectTemplateBuilder( ...@@ -683,6 +684,7 @@ gin::ObjectTemplateBuilder NewTabPageBindings::GetObjectTemplateBuilder(
.SetMethod("getMostVisitedItemData", .SetMethod("getMostVisitedItemData",
&NewTabPageBindings::GetMostVisitedItemData) &NewTabPageBindings::GetMostVisitedItemData)
.SetMethod("updateCustomLink", &NewTabPageBindings::UpdateCustomLink) .SetMethod("updateCustomLink", &NewTabPageBindings::UpdateCustomLink)
.SetMethod("reorderCustomLink", &NewTabPageBindings::ReorderCustomLink)
.SetMethod("undoCustomLinkAction", .SetMethod("undoCustomLinkAction",
&NewTabPageBindings::UndoCustomLinkAction) &NewTabPageBindings::UndoCustomLinkAction)
.SetMethod("resetCustomLinks", &NewTabPageBindings::ResetCustomLinks) .SetMethod("resetCustomLinks", &NewTabPageBindings::ResetCustomLinks)
...@@ -888,6 +890,16 @@ void NewTabPageBindings::UpdateCustomLink(int rid, ...@@ -888,6 +890,16 @@ void NewTabPageBindings::UpdateCustomLink(int rid,
} }
} }
// static
void NewTabPageBindings::ReorderCustomLink(int rid, int new_pos) {
if (!ntp_tiles::IsCustomLinksEnabled())
return;
SearchBox* search_box = GetSearchBoxForCurrentContext();
if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
return;
search_box->ReorderCustomLink(rid, new_pos);
}
// static // static
void NewTabPageBindings::UndoCustomLinkAction() { void NewTabPageBindings::UndoCustomLinkAction() {
if (!ntp_tiles::IsCustomLinksEnabled()) if (!ntp_tiles::IsCustomLinksEnabled())
......
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