diff --git a/browser/base/content/test/newtab/browser_newtab_bug991111.js b/browser/base/content/test/newtab/browser_newtab_bug991111.js index e9932094ca88..ce36ef2bc29a 100644 --- a/browser/base/content/test/newtab/browser_newtab_bug991111.js +++ b/browser/base/content/test/newtab/browser_newtab_bug991111.js @@ -1,9 +1,15 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +const PREF_NEWTAB_ROWS = "browser.newtabpage.rows"; + function runTests() { + // set max rows to 1, to avoid scroll events by clicking middle button + Services.prefs.setIntPref(PREF_NEWTAB_ROWS, 1); yield setLinks("-1"); yield addNewTabPageTab(); + // we need a second newtab to honor max rows + yield addNewTabPageTab(); // Remember if the click handler was triggered let cell = getCell(0); @@ -16,4 +22,5 @@ function runTests() { // Send a middle-click and make sure it happened yield EventUtils.synthesizeMouseAtCenter(cell.node, {button: 1}, getContentWindow()); ok(clicked, "middle click triggered click listener"); + Services.prefs.clearUserPref(PREF_NEWTAB_ROWS); } diff --git a/browser/base/content/test/newtab/browser_newtab_bug998387.js b/browser/base/content/test/newtab/browser_newtab_bug998387.js index d2e51e7d1bd5..04ff21294ab4 100644 --- a/browser/base/content/test/newtab/browser_newtab_bug998387.js +++ b/browser/base/content/test/newtab/browser_newtab_bug998387.js @@ -1,9 +1,15 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +const PREF_NEWTAB_ROWS = "browser.newtabpage.rows"; + function runTests() { + // set max rows to 1, to avoid scroll events by clicking middle button + Services.prefs.setIntPref(PREF_NEWTAB_ROWS, 1); yield setLinks("0"); yield addNewTabPageTab(); + // we need a second newtab to honor max rows + yield addNewTabPageTab(); // Remember if the click handler was triggered let {site} = getCell(0); @@ -22,4 +28,5 @@ function runTests() { // Make sure the cell didn't actually get blocked checkGrid("0"); + Services.prefs.clearUserPref(PREF_NEWTAB_ROWS); } diff --git a/browser/base/content/test/newtab/browser_newtab_focus.js b/browser/base/content/test/newtab/browser_newtab_focus.js index a423e3397801..82d9b4237bc8 100644 --- a/browser/base/content/test/newtab/browser_newtab_focus.js +++ b/browser/base/content/test/newtab/browser_newtab_focus.js @@ -10,7 +10,8 @@ function runTests() { // Focus count in new tab page. // 30 = 9 * 3 + 3 = 9 sites, each with link, pin and remove buttons; search - // bar; search button; and toggle button. + // bar; search button; and toggle button. Additionaly there may or may not be + // a scroll bar caused by fix to 1180387, which will eat an extra focus let FOCUS_COUNT = 30; // Create a new tab page. @@ -42,7 +43,9 @@ function countFocus(aExpectedCount) { let focusedElement = document.commandDispatcher.focusedElement; if (focusedElement && focusedElement.classList.contains("urlbar-input")) { window.removeEventListener("focus", onFocus, true); - is(focusCount, aExpectedCount, "Validate focus count in the new tab page."); + // account for a potential presence of a scroll bar + ok(focusCount == aExpectedCount || focusCount == (aExpectedCount + 1), + "Validate focus count in the new tab page."); executeSoon(TestRunner.next); } else { if (focusedElement && focusedElement.ownerDocument == contentDoc && diff --git a/browser/base/content/test/newtab/head.js b/browser/base/content/test/newtab/head.js index e936ad75b0dd..6eb54ebf03f9 100644 --- a/browser/base/content/test/newtab/head.js +++ b/browser/base/content/test/newtab/head.js @@ -587,7 +587,8 @@ function createExternalDropIframe() { iframe.style.position = "absolute"; iframe.style.zIndex = 50; - let margin = doc.getElementById("newtab-margin-top"); + // the frame has to be attached to a visible element + let margin = doc.getElementById("newtab-search-container"); margin.appendChild(iframe); iframe.addEventListener("load", function onLoad() { diff --git a/browser/components/migration/MigrationUtils.jsm b/browser/components/migration/MigrationUtils.jsm index 862a65777059..e0febf7da9ff 100644 --- a/browser/components/migration/MigrationUtils.jsm +++ b/browser/components/migration/MigrationUtils.jsm @@ -457,6 +457,7 @@ this.MigrationUtils = Object.freeze({ * * @param aKey internal name of the migration source. * Supported values: ie (windows), + * edge (windows), * safari (mac/windows), * canary (mac/windows), * chrome (mac/windows/linux), diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index e68059c1e7a6..a9ce916744c5 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -489,6 +489,7 @@ @RESPATH@/browser/components/FirefoxProfileMigrator.js #ifdef XP_WIN @RESPATH@/browser/components/360seProfileMigrator.js +@RESPATH@/browser/components/EdgeProfileMigrator.js @RESPATH@/browser/components/IEProfileMigrator.js @RESPATH@/browser/components/SafariProfileMigrator.js #endif diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 95c3d8ac5eac..759cca1bb97d 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -61,6 +61,10 @@ background-color: ThreeDShadow; } +#navigator-toolbox:-moz-lwtheme::after { + background-color: rgba(0,0,0,.3); +} + #navigator-toolbox > toolbar:not(:-moz-lwtheme):not(#toolbar-menubar):not(#TabsToolbar) { -moz-appearance: none; border-style: none; diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index be2e3dc41903..021fca9b6855 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -107,19 +107,23 @@ @media (-moz-windows-default-theme) { @media (-moz-os-version: windows-vista), (-moz-os-version: windows-win7) { - #navigator-toolbox:not(:-moz-lwtheme)::after { + #navigator-toolbox::after { background-color: #aabccf; } } @media (-moz-os-version: windows-win8), (-moz-os-version: windows-win10) { - #navigator-toolbox:not(:-moz-lwtheme)::after { + #navigator-toolbox::after { background-color: #c2c2c2; } } } +#navigator-toolbox:-moz-lwtheme::after { + background-color: rgba(0,0,0,.3); +} + #navigator-toolbox > toolbar { -moz-appearance: none; border-style: none; diff --git a/mobile/android/base/db/BrowserContract.java b/mobile/android/base/db/BrowserContract.java index 7cc275984123..f764310508e5 100644 --- a/mobile/android/base/db/BrowserContract.java +++ b/mobile/android/base/db/BrowserContract.java @@ -415,7 +415,7 @@ public class BrowserContract { public static final String DEFAULT_SORT_ORDER = CLIENT_LAST_MODIFIED + " DESC"; - public static final String[] DEFAULT_PROJECTION = new String[] { _ID, URL, TITLE, EXCERPT, WORD_COUNT }; + public static final String[] DEFAULT_PROJECTION = new String[] { _ID, URL, TITLE, EXCERPT, WORD_COUNT, IS_UNREAD }; // Minimum fields required to create a reading list item. public static final String[] REQUIRED_FIELDS = { ReadingListItems.URL, ReadingListItems.TITLE }; diff --git a/mobile/android/base/db/LocalReadingListAccessor.java b/mobile/android/base/db/LocalReadingListAccessor.java index 850e313943b8..ed468339cbb3 100644 --- a/mobile/android/base/db/LocalReadingListAccessor.java +++ b/mobile/android/base/db/LocalReadingListAccessor.java @@ -189,6 +189,14 @@ public class LocalReadingListAccessor implements ReadingListAccessor { cr.update(mReadingListUriWithProfile, values, ReadingListItems._ID + " = " + itemID, null); } + @Override + public void markAsUnread(ContentResolver cr, long itemID) { + final ContentValues values = new ContentValues(); + values.put(ReadingListItems.IS_UNREAD, 1); + + cr.update(mReadingListUriWithProfile, values, ReadingListItems._ID + " = " + itemID, null); + } + @Override public void updateContent(ContentResolver cr, long itemID, String resolvedTitle, String resolvedURL, String excerpt) { final ContentValues values = new ContentValues(); diff --git a/mobile/android/base/db/ReadingListAccessor.java b/mobile/android/base/db/ReadingListAccessor.java index b1ff8fdc2cd3..66682f6ab368 100644 --- a/mobile/android/base/db/ReadingListAccessor.java +++ b/mobile/android/base/db/ReadingListAccessor.java @@ -37,6 +37,7 @@ public interface ReadingListAccessor { void registerContentObserver(Context context, ContentObserver observer); void markAsRead(ContentResolver cr, long itemID); + void markAsUnread(ContentResolver cr, long itemID); void updateContent(ContentResolver cr, long itemID, String resolvedTitle, String resolvedURL, String excerpt); void deleteItem(ContentResolver cr, long itemID); } diff --git a/mobile/android/base/db/StubBrowserDB.java b/mobile/android/base/db/StubBrowserDB.java index 877074cf7269..8cf55767c63d 100644 --- a/mobile/android/base/db/StubBrowserDB.java +++ b/mobile/android/base/db/StubBrowserDB.java @@ -73,6 +73,10 @@ class StubReadingListAccessor implements ReadingListAccessor { public void markAsRead(ContentResolver cr, long itemID) { } + @Override + public void markAsUnread(ContentResolver cr, long itemID) { + } + @Override public void updateContent(ContentResolver cr, long itemID, String resolvedTitle, String resolvedURL, String excerpt) { } diff --git a/mobile/android/base/home/HomeContextMenuInfo.java b/mobile/android/base/home/HomeContextMenuInfo.java index cfdce3ee4e22..c0ba23a83fe0 100644 --- a/mobile/android/base/home/HomeContextMenuInfo.java +++ b/mobile/android/base/home/HomeContextMenuInfo.java @@ -25,6 +25,7 @@ public class HomeContextMenuInfo extends AdapterContextMenuInfo { public int historyId = -1; public int bookmarkId = -1; public int readingListItemId = -1; + public boolean isUnread; public RemoveItemType itemType = null; // Item type to be handled with "Remove" selection. diff --git a/mobile/android/base/home/HomeFragment.java b/mobile/android/base/home/HomeFragment.java index f6fbdd2fa154..15775a87dd9f 100644 --- a/mobile/android/base/home/HomeFragment.java +++ b/mobile/android/base/home/HomeFragment.java @@ -155,6 +155,9 @@ public abstract class HomeFragment extends Fragment { if (!RestrictedProfiles.isAllowed(view.getContext(), Restriction.DISALLOW_PRIVATE_BROWSING)) { menu.findItem(R.id.home_open_private_tab).setVisible(false); } + + menu.findItem(R.id.mark_read).setVisible(info.isInReadingList() && info.isUnread); + menu.findItem(R.id.mark_unread).setVisible(info.isInReadingList() && !info.isUnread); } @Override @@ -253,6 +256,22 @@ public abstract class HomeFragment extends Fragment { return true; } + if (itemId == R.id.mark_read) { + GeckoProfile + .get(context) + .getDB() + .getReadingListAccessor() + .markAsRead(context.getContentResolver(), info.id); + } + + if (itemId == R.id.mark_unread) { + GeckoProfile + .get(context) + .getDB() + .getReadingListAccessor() + .markAsUnread(context.getContentResolver(), info.id); + } + return false; } diff --git a/mobile/android/base/home/ReadingListPanel.java b/mobile/android/base/home/ReadingListPanel.java index 9a826596a23b..64575f240ea5 100644 --- a/mobile/android/base/home/ReadingListPanel.java +++ b/mobile/android/base/home/ReadingListPanel.java @@ -90,6 +90,8 @@ public class ReadingListPanel extends HomeFragment { // This item is a TwoLinePageRow, so we allow switch-to-tab. mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB)); + + markAsRead(id); } }); @@ -100,6 +102,7 @@ public class ReadingListPanel extends HomeFragment { info.url = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.URL)); info.title = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.TITLE)); info.readingListItemId = cursor.getInt(cursor.getColumnIndexOrThrow(ReadingListItems._ID)); + info.isUnread = cursor.getInt(cursor.getColumnIndexOrThrow(ReadingListItems.IS_UNREAD)) == 1; info.itemType = RemoveItemType.READING_LIST; return info; } @@ -107,6 +110,15 @@ public class ReadingListPanel extends HomeFragment { registerForContextMenu(mList); } + private void markAsRead(long id) { + final Context context = getActivity(); + + GeckoProfile.get(context).getDB().getReadingListAccessor().markAsRead( + context.getContentResolver(), + id + ); + } + @Override public void onDestroyView() { super.onDestroyView(); diff --git a/mobile/android/base/home/ReadingListRow.java b/mobile/android/base/home/ReadingListRow.java index 03659fe80615..a32739b112f3 100644 --- a/mobile/android/base/home/ReadingListRow.java +++ b/mobile/android/base/home/ReadingListRow.java @@ -18,6 +18,7 @@ import android.database.Cursor; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -28,6 +29,7 @@ public class ReadingListRow extends LinearLayout { private final TextView title; private final TextView excerpt; private final TextView readTime; + private final ImageView indicator; // Average reading speed in words per minute. private static final int AVERAGE_READING_SPEED = 250; @@ -50,6 +52,7 @@ public class ReadingListRow extends LinearLayout { title = (TextView) findViewById(R.id.title); excerpt = (TextView) findViewById(R.id.excerpt); readTime = (TextView) findViewById(R.id.read_time); + indicator = (ImageView) findViewById(R.id.indicator); } public void updateFromCursor(Cursor cursor) { @@ -57,13 +60,19 @@ public class ReadingListRow extends LinearLayout { return; } + final boolean isUnread = cursor.getInt(cursor.getColumnIndexOrThrow(ReadingListItems.IS_UNREAD)) == 1; + final String url = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.URL)); final String titleText = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.TITLE)); title.setText(TextUtils.isEmpty(titleText) ? StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url)) : titleText); + title.setTextAppearance(getContext(), isUnread ? R.style.Widget_ReadingListRow_Title_Unread : R.style.Widget_ReadingListRow_Title_Read); final String excerptText = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.EXCERPT)); excerpt.setText(TextUtils.isEmpty(excerptText) ? url : excerptText); + excerpt.setTextAppearance(getContext(), isUnread ? R.style.Widget_ReadingListRow_Title_Unread : R.style.Widget_ReadingListRow_Title_Read); + + indicator.setImageResource(isUnread ? R.drawable.reading_list_indicator_unread : R.drawable.reading_list_indicator_read); /* Disabled until UX issues are fixed (see bug 1110461). final int lengthIndex = cursor.getColumnIndexOrThrow(ReadingListItems.LENGTH); diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index d3a8c548d3c8..22ef325f1cb0 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -391,6 +391,8 @@ size. --> + + + + + + + + + + + diff --git a/mobile/android/base/resources/drawable/reading_list_indicator_unread.xml b/mobile/android/base/resources/drawable/reading_list_indicator_unread.xml new file mode 100644 index 000000000000..f052902efc67 --- /dev/null +++ b/mobile/android/base/resources/drawable/reading_list_indicator_unread.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/mobile/android/base/resources/layout/reading_list_row_view.xml b/mobile/android/base/resources/layout/reading_list_row_view.xml index 6bb799d6cbe4..a8ded1dfeef7 100644 --- a/mobile/android/base/resources/layout/reading_list_row_view.xml +++ b/mobile/android/base/resources/layout/reading_list_row_view.xml @@ -5,11 +5,16 @@ + + diff --git a/mobile/android/base/resources/menu/home_contextmenu.xml b/mobile/android/base/resources/menu/home_contextmenu.xml index 294b8aee5086..5fb6964765b6 100644 --- a/mobile/android/base/resources/menu/home_contextmenu.xml +++ b/mobile/android/base/resources/menu/home_contextmenu.xml @@ -29,6 +29,12 @@ + + + + diff --git a/mobile/android/base/resources/values/dimens.xml b/mobile/android/base/resources/values/dimens.xml index f2fa452d702d..1487a12240cd 100644 --- a/mobile/android/base/resources/values/dimens.xml +++ b/mobile/android/base/resources/values/dimens.xml @@ -86,7 +86,6 @@ 128dp - 15dp 10dp diff --git a/mobile/android/base/resources/values/styles.xml b/mobile/android/base/resources/values/styles.xml index 577e5d93bbc2..baa1fa8bcf39 100644 --- a/mobile/android/base/resources/values/styles.xml +++ b/mobile/android/base/resources/values/styles.xml @@ -137,6 +137,14 @@ end + + + + + + + + diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index bb8353bc2a90..659af713ed75 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -360,6 +360,8 @@ &contextmenu_top_sites_pin; &contextmenu_top_sites_unpin; &contextmenu_add_search_engine; + &contextmenu_mark_read; + &contextmenu_mark_unread; &doorhanger_login_no_username; &doorhanger_login_edit_title; diff --git a/mobile/android/locales/en-US/chrome/aboutPrivateBrowsing.dtd b/mobile/android/locales/en-US/chrome/aboutPrivateBrowsing.dtd index 7e29d554e1ee..95fec647497e 100644 --- a/mobile/android/locales/en-US/chrome/aboutPrivateBrowsing.dtd +++ b/mobile/android/locales/en-US/chrome/aboutPrivateBrowsing.dtd @@ -8,9 +8,15 @@ is used as a title, with the privatebrowsingpage.title string preceding it but on a separate line. So the final line will say "Private Browsing + Tracking Protection". --> + +