Bug 1396282 - Add query for getting Highlights (recent bookmarks and recent history with metadata). r=mak
Adds index to moz_bookmarks.dateAdded for use by Highlights query for recent bookmarks.
MozReview-Commit-ID: 7Gs8H0kUij2
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -1106,18 +1106,26 @@ Database::InitSchema(bool* aDatabaseMigr
}
// Firefox 55 uses schema version 37.
if (currentSchemaVersion < 38) {
rv = MigrateV38Up();
NS_ENSURE_SUCCESS(rv, rv);
}
+
// Firefox 56 uses schema version 38.
+ if (currentSchemaVersion < 39) {
+ rv = MigrateV39Up();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Firefox 57 uses schema version 39.
+
// Schema Upgrades must add migration code here.
rv = UpdateBookmarkRootTitles();
// We don't want a broken localization to cause us to think
// the database is corrupt and needs to be replaced.
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
@@ -1164,16 +1172,18 @@ Database::InitSchema(bool* aDatabaseMigr
rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_DELETED);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACETYPE);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PARENTPOSITION);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
NS_ENSURE_SUCCESS(rv, rv);
+ rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_DATEADDED);
+ NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
NS_ENSURE_SUCCESS(rv, rv);
// moz_keywords.
rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_KEYWORDS_PLACEPOSTDATA);
NS_ENSURE_SUCCESS(rv, rv);
@@ -2320,16 +2330,27 @@ Database::MigrateV38Up()
));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult
+Database::MigrateV39Up() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Create an index on dateAdded.
+ nsresult rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_DATEADDED);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
Database::GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType,
nsTArray<int64_t>& aItemIds)
{
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT b.id FROM moz_items_annos a "
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
"JOIN moz_bookmarks b ON b.id = a.item_id "
--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -14,17 +14,17 @@
#include "mozilla/storage/StatementCache.h"
#include "mozilla/Attributes.h"
#include "nsIEventTarget.h"
#include "Shutdown.h"
#include "nsCategoryCache.h"
// This is the schema version. Update it at any schema change and add a
// corresponding migrateVxx method below.
-#define DATABASE_SCHEMA_VERSION 38
+#define DATABASE_SCHEMA_VERSION 39
// Fired after Places inited.
#define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
// This topic is received when the profile is about to be lost. Places does
// initial shutdown work and notifies TOPIC_PLACES_SHUTDOWN to all listeners.
// Any shutdown work that requires the Places APIs should happen here.
#define TOPIC_PROFILE_CHANGE_TEARDOWN "profile-change-teardown"
// Fired when Places is shutting down. Any code should stop accessing Places
@@ -298,16 +298,17 @@ protected:
nsresult MigrateV31Up();
nsresult MigrateV32Up();
nsresult MigrateV33Up();
nsresult MigrateV34Up();
nsresult MigrateV35Up();
nsresult MigrateV36Up();
nsresult MigrateV37Up();
nsresult MigrateV38Up();
+ nsresult MigrateV39Up();
nsresult UpdateBookmarkRootTitles();
friend class ConnectionShutdownBlocker;
int64_t CreateMobileRoot();
nsresult GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType,
nsTArray<int64_t>& aItemIds);
--- a/toolkit/components/places/nsPlacesIndexes.h
+++ b/toolkit/components/places/nsPlacesIndexes.h
@@ -84,16 +84,21 @@
"parentindex", "moz_bookmarks", "parent, position", "" \
)
#define CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED \
CREATE_PLACES_IDX( \
"itemlastmodifiedindex", "moz_bookmarks", "fk, lastModified", "" \
)
+#define CREATE_IDX_MOZ_BOOKMARKS_DATEADDED \
+ CREATE_PLACES_IDX( \
+ "dateaddedindex", "moz_bookmarks", "dateAdded", "" \
+ )
+
#define CREATE_IDX_MOZ_BOOKMARKS_GUID \
CREATE_PLACES_IDX( \
"guid_uniqueindex", "moz_bookmarks", "guid", "UNIQUE" \
)
/**
* moz_annos
*/
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -1,17 +1,17 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// It is expected that the test files importing this file define Cu etc.
/* global Cu, Ci, Cc, Cr */
-const CURRENT_SCHEMA_VERSION = 38;
+const CURRENT_SCHEMA_VERSION = 39;
const FIRST_UPGRADABLE_SCHEMA_VERSION = 11;
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
// Shortcuts to transitions type.
const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
new file mode 100644
index 0000000000000000000000000000000000000000..65fc6f0109747e92258e549bce90153fbc32365d
GIT binary patch
literal 1146880
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
z$J;mVKztqczx-Mv`myNHjX%5bLpQ$d`WLR(uHXOi@5ey^00000000000000000000
z0000000000000000O0GvQ~etgXA&Fxa(#V?t+{L>(fVWY?`{4~_QxN6cRj2%9v^w+
z-Ic<Xvq62n+^9c3vU_;vRHZupXk+j0Mrk^c_{&Gej(v8=`*Ir-2Ud>L8esn|{m(pI
zIeq4lWA%NPPG4;HfAsf$>a{a{8xw~Td;0qNQgQ#DL}I4Vn5(~c*RFV+YS0+2gx5;t
za$z{EP4Akng~Nq%xpXb83^#&mp*%O7cy%hA9y@n<UwC2c@YRO~PhP+KUFF_QiJQHN
z2Ud<*DNRh28pD%eW!Dq+pmsT^JzlC!cF*zd$(cg68k8U3`tY7=X?mtnzFG+<ha1g~
zm3X#^<71akOkb={T{(E>p}{9E-TR)0vl|l+CK3yi#T!tV2<IE`ovan=GeJG^>e;bt
zV^3cUr-J#D&m|Hc`H7#+|H|tx^~5W@d$qGJ6pLZCzU-`n>dvuqyQWIDU@E-QPS~}u
zw4IHh-Uw<t8(~<kFWa7aH7phj@q!N=J~&f6G;w)$Pj&A@gRky>dh}vuW8!GyvG&}_
zuDOfB<)9qS1+_Idy%x-c<v6Pt4}5I=T=Ch$?6oV;pWl~yXz<5>c;;izrZ*;zB=)Tw
zd18LLJ>Xgcx60}HiRKPh>a~mGmma>jXa8gIxS#pb^Y4FuYGdM5;)#{JHBk<yhZpu@
z&9SeR8#B|T>hN?}ZjOKa$nK|4Rh~L^;^L|O4-J0o&6~gdJ;{xU@x;!R!@v0Ai>rJS
zlVP<{D^1Kdf@RY*lM}D*e)Q=1{bMH%&z2r}?4iM_-~8xH1I@ebl{<X50{{R300000
z00000000000000000000000000002M9X8NBymE&Z0RR9100000000000000000000
z000000000000000cUXV(@X8%t1ONa400000000000000000000000000000000000
z++hRpPjB>OBKouFSEHYfUXA9W?}@7b00000000000000000000000000000000000
z008j!^7h_rVkFg<m@5~mL1VZQUMrQ$h2gL^op^OHn@wzq+bX4riBe;DGOQ$CeS1$k
zzfdfO)%x=M`!d<YzPPOzTn@_NTu@uyd~Z6N*c&%b%ulzY%UbiPY~t}|YdM@Au9l`}
z8s)3YGww=e6Av_((;e)O&!IOeCZa!yJ{|pB^x^0SqL<<-00000000000000000000
z0000000000000000002|KlknFk>p&tPz@TxmGD}rTrLcUwdvjs=?8LM^$qDQ$x3Nr
zqSP3k3@h1t()*Id;Brt7=Ym>yzs%dxdy^CM)9pZ4L;BwI<H?C~I6Yh~P0uvSSGyZi
z%|#T7#jslM4)+Yi>l=LO6N%`zqSvB}(YtSa@y5q*T)Q!H{Y%$>=KAFI`(OV3mp}aS
zcfXu}>3_sY00000000000000000000000000000000000008iHYRBM)L^6@g#UHcT
zt+`w}mFT$`yf*mh-JP;}Z07Xkr;p8Ed9-kC|K<&eTr%1BKs!6xpE?^%2GvIXd||rY
z>agkF*V`RG{>kylXZKus=FB4p_KX}(J+`UcbL&FST>3;<Z{(jX)k}?_*y^(3jo;}E
zF?s2kt20MR7xp|AoKAh$##Yzl_Ey)vWM5zQbnCC6=TEf%0C}s|`rU7|%ST=>kJJyH
zsf>@D9vi)oI=rFX`>}=Iy?s50OZCb5dOd%#{pZc|VKsmB?4fq=9bf2-_RP6PW9Q}Z
z@zV63tEu-5v`5*wFiN&BbD&(#Uk&GL`N>+LJ`=Q0fBm+vb;>g*pE~{Am1EEDpPD?I
zI=H^wfA2#7p1yQv_VYo#5!CYM!?3(C#@0=pF;0z^>$M{<yzu<USR-|0U3-it7RJc*
zr4}|If4K7p-t#Ae#*1NXwmraoPqxb~+jgIPVfwj!js2B#7pBus_O}Mg?O7No-PalC
z;QVy{e627!8`Ro8@446+sWAWib2G=zT)Hqjc{qI_*Zwv;r<3Y?s#FW6!Yhk=+Wh<R
z?XGvvb;|Rj2g^qeUE5te_VB*ck-pZ2w|F+GRO;3-&Ia{(e<wRzyZM8iuckD8;c(;l
z=-8zb7cceg?~T_IUr<{+XLBUJGLBX+hvmx)7uNdRCpsgXIC|woqj2Wr%+bg8rXJ2V
zmyk=|vvBRFGL>+mR1TWSAA0kFnNHb`j<<YQPrJ`U3)gWfeX21N@B6_poUIgUv-R%I
zudVyL#C&xs?p$njPVLULd*9ueG1a+oy60=evGE0ur&~R8yE^kNPSm<$CJMD>6UO&Y
zqaAMb?s;#j)xB@~%I+tE>in|4@x5M&W1X3M9!fU*_Vo>P7JF-UEeso-?1ARP<dr-7
zECT=l00000000000000000000000000000000000fIF?fd3faxF9HAn0000000000
z00000000000000000000000000Pe7X_$L+pSt9zY_y7O^00000000000000000000
z0000000000000000N?y_sh-4L$wZ}C&GaRE5}T8WiEHWpxFHpNB@z8id;kCd00000
z000000000000000000000000000000fNvSS>ARAZVl~y1-kh7bHV{XW(U%hOe*gdg
z00000000000000000000000000000000000@GW9fGQBlf39pUM23KDUYsGqMAdaS^
zpH4)79DOP-000000000000000000000000000000000000002s>)lW~o!EQ2T$l{%
zyUtY#wZ_3PoQ=!bEva<k=)^+hbS<0<YK>ByR1E5qwbERp6jq0lao<WfQ7Q+!I+^ve
zVc6)82Tn&nlZgH}`czy100000000000000000000000000000000000006+(y`l8p
zTqT?+m4jUqVK`eU)Mo2v!?5wr^wDG|aJm-G1+_*gsP8HU^~qXku2BlBskf!~CQp|O
zlX3W5oYpwFke(iB4*bd;J~03Q000000000000000000000000000000000000Jy^j
z;-5^kEfM`mbUiv7e*gdg00000000000000000000000000000000000fZN!X=}#n+
zTl$j8Y<6?{Y%m#A8|Mqt^-Ma^b1`^r@Z+ECl-twI%m-STo6;x3dgIwrz0?Sb3#ps#
zeZ5mYpIR5E-fZWlQwNI0{6rYeRtmM*dcG0n8#ASPekv>%gIcp%4}v%pRPx1AeXd-%
z8Wi(kwHe7DT<mo|49gRR+QMAxdw!!+-kpq>mD|{t%w;m!k<5qQd|;;eGZ3HK!1Wu6
z=s!mviR#hOXv>YS-uUE=AG~qq#{Jj7eEna?O#lD@0000000000000000000000000
z0000000000{~tU&klFgoU6t_K_-t_X#jsYa*Iy`?8o_vBz7dwH#o)?#cjNfT!}*c#
zT%Xx`X{bBAEM=}-m<;B^dZSoq6vpSPr5EOd)<|*J{K)XS%!A)~PqV|~ADUlQ8ILEg
zw9-fR=O4~Lyt_a1;1jpYpDWaYYNONRk^IPebD0Ovz10lmLcLK5i>0YjQ0$s*q%ZT}
z=-s#J8=q59C>Faq?di=tc=UE%rsqq=?rn%Wjl3tD+46X+Q){>COP|K)Yvu8oLVc#4
zyE{Mf?w-t+y{qTW#Jk+ddw5$Wvt{4Vs(F`7^-^OpoUb;T1MYrLdd=CVYQbbsoxIu^
zb?@#}X3LS)M{Vw5YvkspEG~EV&SYlGq18uUu@!NCe|#18M?aW|z8d{$^o8iN(SL}3
zIr@0?lhKdHK>z>%00000000000000000000000000000000000xD(c=v&lr_FCQ5@
z_Sqf%oyw!X_fxOs+Le#|#Lwn`<@IcTx+nS2;Ag({{QJ}Gj~{#U=5MFkAAkIZXFir`
ze|&ZK)1y7PbWiS~!KvT;=u7S3$?JE&tFQg>iA(psr?)?zwm*705&doS)#%I7XQN+;
zek}Td=yG&14gvrG00000000000000000000000000000000000z~9rx^aIICI62%X
zO-z(3!@c*VpGZ#E!r?->T)GxkhOd?zGt;H&@N`(77*5@l-kIxeeeuN?hlkR8mi1@^
z)k1l0xbN0@y&4vah2fsJrypAuuLW~qIk+4Yhcox2_bqE`mDBSR!|9&%#^i8+>zK==
z6Ul6Uy!QU+*Avm3(O*YjiT*tLV)Td6=cC_^J`)E40000000000000000000000000
z0000000000007_{QzqS)>~9`&%|l=F(Azv@n}?p}A=5mhn}<|7-Iwc+JM~AuoQVE5
z`m5-FMSl|gLG*jkXQE$={(T$-00000000000000000000000000000000000004k*
z4B2!lnRvCYUG%n#Y`f@b7nydEZWpPZbSf7I(|yVQ<{{S~57Hm~dLnu=`s?T`(Vs_O
zjQ%kCeDu4~XW}3L00000000000000000000000000000000000004Yr%A^y?{^lXq
zJoGgWz0E_mdFW{#GR;G}c}S(xiCll&sXw}qh`t>Ce)ONBUy6Pv`sdLPL|3DkI0yg$
z000000000000000000000000000000000000RMM3rMD)h!s)SdhxdgS#tvWYYX&AN
z_1eYpOAlY%v;VPlD{y@5@`>q-)u}57&-69}XUDFMJ$*5p3g%Bfmu&{lJY6|`=8<Fd
zeV0yO%ryhYkL-T>ROP8tCoZ1apK1n<jh`z%TbR9e<@xjbGR?q&!v|-IhbAu1?y2tW
z*_hs%+x_U#^ZUn69G)#b@>nxr=TxOS{%B+G?nY_4zrVTKSMKm`0RR910000000000
z000000000000000000000002M9oFAGymE&Z0RR910000000000000000000000000
z0000000000cUXV(@X8%t1ONa400000000000000000000000000000000000++qFk
z&tUX@iRf>lzli>4^xvb;NB<@Ijp$dSe;55?^z+fjqMwXD8htqWXVE{2lK=n!00000
z000000000000000000000000000000;2%&nlguUiH@Ax1rdH9nu~qbLXcgIkR?)M*
zRb<w+igbUgNacDmxi~YMNhFi$!B&y#YZuve(bFz6?IPVSQay2zOZCU|4@R#fqQ8y4
z8httXV)Wml&qco#eLDJ7^h?pdiT+jeFQbn}KN9`Z=!0<*00000000000000000000
z0000000000000000002|1L@5qlgYm9#&+4Wp<QMM+GTouyG*UiW|FyNUw>TmCHr!%
zTDrGYOK)oBq*8sYXlk$(O>NF*a>-;S+bYsMts<4_$>idW@iETGWcvDY{qY(GZ@ig^
zz7qXW^xM%J(Jw?l8+|1Dq3C;~MifNPMn|Ik(e~)=(YiPZ00000000000000000000
z000000000000000004k*N*glCY_dPmb1`^r@Z+D{+^XhU)lK)lzNuC1YgN}j^7_VB
zwYOc}{l<n?HQTOk``SRO+S947Z&fqx>XvQmTGe#Bdf$`%t!k=Wy?ZV<kjZ6}?b_xK
z_6}qc3$?*d?;c!MORbM<xlV1VX~P@8lU}ODtLs~;t^2z~&tmOEZ$2=ST~^EV$NMlC
z4JD$#jQ%+KT=Z+vC!?Q>{zdfR=+!8SYEckfjGl?!7mY;kijx2U000000000000000
z000000000000000000000Qgq3KAlY_dM*a94Sss};9_Ody{~UxtZaDWcQ!3n*6)5}
z<5FeE7d9+awta12sj_v``lZT!Pp(_4+;g#isdD#RZmF{QgMCYt+$VZFl@Go7z)ZHg
z($ihZbXU^dmDIX)HrJ{2$0s{@<JS|>m!r=|zZU&s^wZIgM*ldv5=}?XMx)Uq(F4(7
zl)CZ1Z+sz60ssI20000000000000000000000000000000002s8^xyd*5rJ3s#Fe&
z<4rxyKqCyx6NOqUkZuJE)AjKMnT_eKxmp-DI)P?F_1Mhm%TFJhz4B<`+F&zK2`Az)
z+X>lLAgIoFdZe0x@<{#AnacRc>9NrZ8=HaB^o7HX<D+AjPF%dy*9=TvdgkiPk<x`d
zPX(tpH3NnD=bxK7cIMKB*~!EG&A|BNvwJQ*bLNo)dqxg#ZU)Ye9xNX@bZvL>*u(pB
z&A^$HPn~}5%CTqnPfea3Xa-J=mg}`6FTC*l$XH{2GjQVQl@pD^nUga|AKTm83_NqL
z(b#!;e7rQh=jw)LVE4%vrk~r_*k3t!VR~JEylJWEFA~w;#0LNX000000000000000
z000000000000000000000QlCC%_I}aOsXd?a_RnN<dr+TA^-pY000000000000000
z00000000000000000000xWoG6pG@=ziRf$5SE4_SKL7v#00000000000000000000
q000000000000000z&EAdOfs3=oX)h$P3d$`CYj4+TeWm5^?v}7<5>a#
--- a/toolkit/components/places/tests/migration/xpcshell.ini
+++ b/toolkit/components/places/tests/migration/xpcshell.ini
@@ -19,16 +19,17 @@ support-files =
places_v31.sqlite
places_v32.sqlite
places_v33.sqlite
places_v34.sqlite
places_v35.sqlite
places_v36.sqlite
places_v37.sqlite
places_v38.sqlite
+ places_v39.sqlite
[test_current_from_downgraded.js]
[test_current_from_v6.js]
[test_current_from_v11.js]
[test_current_from_v19.js]
[test_current_from_v24.js]
[test_current_from_v25.js]
[test_current_from_v26.js]
--- a/toolkit/modules/NewTabUtils.jsm
+++ b/toolkit/modules/NewTabUtils.jsm
@@ -54,16 +54,19 @@ const LINKS_GET_LINKS_LIMIT = 100;
const TOPIC_GATHER_TELEMETRY = "gather-telemetry";
// Some default frecency threshold for Activity Stream requests
const ACTIVITY_STREAM_DEFAULT_FRECENCY = 150;
// Some default query limit for Activity Stream requests
const ACTIVITY_STREAM_DEFAULT_LIMIT = 12;
+// Some default seconds ago for Activity Stream recent requests
+const ACTIVITY_STREAM_DEFAULT_RECENT = 5 * 24 * 60 * 60;
+
/**
* Calculate the MD5 hash for a string.
* @param aValue
* The string to convert.
* @return The base64 representation of the MD5 hash.
*/
function toHash(aValue) {
let value = gUnicodeConverter.convertToByteArray(aValue);
@@ -872,16 +875,38 @@ var ActivityStreamProvider = {
return Object.assign({
bookmarkType: PlacesUtils.bookmarks.TYPE_BOOKMARK,
limit: this._adjustLimitForBlocked(aOptions),
tagsFolderId: PlacesUtils.tagsFolderId
}, aParams);
},
/**
+ * Shared columns for Highlights related queries.
+ */
+ _highlightsColumns: ["bookmarkGuid", "description", "guid",
+ "preview_image_url", "title", "url"],
+
+ /**
+ * Shared post-processing of Highlights links.
+ */
+ _processHighlights(aLinks, aOptions, aType) {
+ // Filter out blocked if necessary
+ if (!aOptions.ignoreBlocked) {
+ aLinks = aLinks.filter(link => !BlockedLinks.isBlocked(link));
+ }
+
+ // Limit the results to the requested number and set a type corresponding to
+ // which query selected it
+ return aLinks.slice(0, aOptions.numItems).map(item => Object.assign(item, {
+ type: aType
+ }));
+ },
+
+ /**
* From an Array of links, if favicons are present, convert to data URIs
*
* @param {Array} aLinks
* an array containing objects with favicon data and mimeTypes
*
* @returns {Array} an array of links with favicons as data uri
*/
_faviconBytesToDataURI(aLinks) {
@@ -937,16 +962,96 @@ var ActivityStreamProvider = {
link.mimeType = null;
return link;
})
));
}
return aLinks;
},
+ /**
+ * Get most-recently-created visited bookmarks for Activity Stream.
+ *
+ * @param {Object} aOptions
+ * {num} bookmarkSecondsAgo: Maximum age of added bookmark.
+ * {bool} ignoreBlocked: Do not filter out blocked links.
+ * {int} numItems: Maximum number of items to return.
+ */
+ async getRecentBookmarks(aOptions) {
+ const options = Object.assign({
+ bookmarkSecondsAgo: ACTIVITY_STREAM_DEFAULT_RECENT,
+ ignoreBlocked: false,
+ numItems: ACTIVITY_STREAM_DEFAULT_LIMIT
+ }, aOptions || {});
+
+ const sqlQuery = `
+ SELECT
+ b.guid AS bookmarkGuid,
+ description,
+ h.guid,
+ preview_image_url,
+ b.title,
+ url
+ FROM moz_bookmarks b
+ JOIN moz_bookmarks p
+ ON p.id = b.parent
+ JOIN moz_places h
+ ON h.id = b.fk
+ WHERE b.dateAdded >= :dateAddedThreshold
+ AND b.title NOTNULL
+ AND b.type = :bookmarkType
+ AND p.parent <> :tagsFolderId
+ ${this._commonPlacesWhere}
+ ORDER BY b.dateAdded DESC
+ LIMIT :limit
+ `;
+
+ return this._processHighlights(await this.executePlacesQuery(sqlQuery, {
+ columns: this._highlightsColumns,
+ params: this._getCommonParams(options, {
+ dateAddedThreshold: (Date.now() - options.bookmarkSecondsAgo * 1000) * 1000
+ })
+ }), options, "bookmark");
+ },
+
+ /**
+ * Get most-recently-visited history with metadata for Activity Stream.
+ *
+ * @param {Object} aOptions
+ * {bool} ignoreBlocked: Do not filter out blocked links.
+ * {int} numItems: Maximum number of items to return.
+ */
+ async getRecentHistory(aOptions) {
+ const options = Object.assign({
+ ignoreBlocked: false,
+ numItems: ACTIVITY_STREAM_DEFAULT_LIMIT,
+ }, aOptions || {});
+
+ const sqlQuery = `
+ SELECT
+ ${this._commonBookmarkGuidSelect},
+ description,
+ guid,
+ preview_image_url,
+ title,
+ url
+ FROM moz_places h
+ WHERE description NOTNULL
+ AND preview_image_url NOTNULL
+ ${this._commonPlacesWhere}
+ ORDER BY last_visit_date DESC
+ LIMIT :limit
+ `;
+
+ return this._processHighlights(await this.executePlacesQuery(sqlQuery, {
+ columns: this._highlightsColumns,
+ params: this._getCommonParams(options)
+ }), options, "history");
+ },
+
/*
* Gets the top frecent sites for Activity Stream.
*
* @param {Object} aOptions
* {bool} ignoreBlocked: Do not filter out blocked links.
* {int} numItems: Maximum number of items to return.
* {int} topsiteFrecency: Minimum amount of frecency for a site.
*
@@ -1176,16 +1281,57 @@ var ActivityStreamLinks = {
*/
deleteHistoryEntry(aUrl) {
const url = aUrl;
PinnedLinks.unpin({url});
return PlacesUtils.history.remove(url);
},
/**
+ * Get the Highlights links to show on Activity Stream
+ *
+ * @param {Object} aOptions
+ * {bool} excludeBookmarks: Don't add bookmark items.
+ * {bool} excludeHistory: Don't add history items.
+ * {int} numItems: Maximum number of (bookmark or history) items to return.
+ *
+ * @return {Promise} Returns a promise with the array of links as the payload
+ */
+ async getHighlights(aOptions = {}) {
+ aOptions.numItems = aOptions.numItems || ACTIVITY_STREAM_DEFAULT_LIMIT;
+ const results = [];
+
+ // First get bookmarks if we want them
+ if (!aOptions.excludeBookmarks) {
+ results.push(...await ActivityStreamProvider.getRecentBookmarks(aOptions));
+ }
+
+ // Add in history if we need more and want them
+ if (aOptions.numItems - results.length > 0 && !aOptions.excludeHistory) {
+ // Use the same numItems as bookmarks above in case we remove duplicates
+ const history = await ActivityStreamProvider.getRecentHistory(aOptions);
+
+ // Only include a url once in the result preferring the bookmark
+ const bookmarkUrls = new Set(results.map(({url}) => url));
+ for (const page of history) {
+ if (!bookmarkUrls.has(page.url)) {
+ results.push(page);
+
+ // Stop adding pages once we reach the desired maximum
+ if (results.length === aOptions.numItems) {
+ break;
+ }
+ }
+ }
+ }
+
+ return results;
+ },
+
+ /**
* Get the top sites to show on Activity Stream
*
* @return {Promise} Returns a promise with the array of links as the payload
*/
async getTopSites(aOptions = {}) {
return ActivityStreamProvider.getTopFrecentSites(aOptions);
}
};
--- a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js
+++ b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js
@@ -359,16 +359,129 @@ add_task(async function addFavicons() {
await PlacesTestUtils.addFavicons(faviconData);
await provider._addFavicons(links);
Assert.equal(links[0].mimeType, "image/png", "Got the right mime type before deleting it");
Assert.equal(links[0].faviconLength, links[0].favicon.length, "Got the right length for the byte array");
Assert.equal(provider._faviconBytesToDataURI(links)[0].favicon, base64URL, "Got the right favicon");
});
+add_task(async function getHighlights() {
+ const addMetadata = url => PlacesUtils.history.update({
+ description: "desc",
+ previewImageURL: "https://image/",
+ url
+ });
+
+ await setUpActivityStreamTest();
+
+ let provider = NewTabUtils.activityStreamLinks;
+ let links = await provider.getHighlights();
+ Assert.equal(links.length, 0, "empty history yields empty links");
+
+ // Add bookmarks
+ const now = Date.now();
+ const oldSeconds = 24 * 60 * 60; // 1 day old
+ let bookmarks = [
+ {
+ dateAdded: new Date(now - oldSeconds * 1000),
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ title: "foo",
+ url: "https://mozilla1.com/dayOld"
+ },
+ {
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ title: "foo",
+ url: "https://mozilla1.com/nowNew"
+ }
+ ];
+ for (let placeInfo of bookmarks) {
+ await PlacesUtils.bookmarks.insert(placeInfo);
+ }
+
+ links = await provider.getHighlights();
+ Assert.equal(links.length, 0, "adding bookmarks without visits doesn't yield more links");
+
+ // Add a history visit
+ let testURI = "http://mozilla.com/";
+ await PlacesTestUtils.addVisits(testURI);
+
+ links = await provider.getHighlights();
+ Assert.equal(links.length, 0, "adding visits without metadata doesn't yield more links");
+
+ // Add bookmark visits
+ for (let placeInfo of bookmarks) {
+ await PlacesTestUtils.addVisits(placeInfo.url);
+ }
+
+ links = await provider.getHighlights();
+ Assert.equal(links.length, 2, "adding visits to bookmarks yields more links");
+ Assert.equal(links[0].url, bookmarks[1].url, "first bookmark is younger bookmark");
+ Assert.equal(links[0].type, "bookmark", "first bookmark is bookmark");
+ Assert.equal(links[1].url, bookmarks[0].url, "second bookmark is older bookmark");
+ Assert.equal(links[1].type, "bookmark", "second bookmark is bookmark");
+
+ // Add metadata to history
+ await addMetadata(testURI);
+
+ links = await provider.getHighlights();
+ Assert.equal(links.length, 3, "adding metadata yield more links");
+ Assert.equal(links[0].url, bookmarks[1].url, "still have younger bookmark");
+ Assert.equal(links[1].url, bookmarks[0].url, "still have older bookmark");
+ Assert.equal(links[2].url, testURI, "added visit corresponds to added url");
+ Assert.equal(links[2].type, "history", "added visit is history");
+
+ links = await provider.getHighlights({numItems: 2});
+ Assert.equal(links.length, 2, "limited to 2 items");
+ Assert.equal(links[0].url, bookmarks[1].url, "still have younger bookmark");
+ Assert.equal(links[1].url, bookmarks[0].url, "still have older bookmark");
+
+ links = await provider.getHighlights({excludeHistory: true});
+ Assert.equal(links.length, 2, "only have bookmarks");
+ Assert.equal(links[0].url, bookmarks[1].url, "still have younger bookmark");
+ Assert.equal(links[1].url, bookmarks[0].url, "still have older bookmark");
+
+ links = await provider.getHighlights({excludeBookmarks: true});
+ Assert.equal(links.length, 1, "only have history");
+ Assert.equal(links[0].url, testURI, "only have the history now");
+
+ links = await provider.getHighlights({excludeBookmarks: true, excludeHistory: true});
+ Assert.equal(links.length, 0, "requested nothing, get nothing");
+
+ links = await provider.getHighlights({bookmarkSecondsAgo: oldSeconds / 2});
+ Assert.equal(links.length, 2, "old bookmark filtered out with");
+ Assert.equal(links[0].url, bookmarks[1].url, "still have newer bookmark");
+ Assert.equal(links[1].url, testURI, "still have the history");
+
+ // Add a visit and metadata to the older bookmark
+ await PlacesTestUtils.addVisits(bookmarks[0].url);
+ await addMetadata(bookmarks[0].url);
+
+ links = await provider.getHighlights({bookmarkSecondsAgo: oldSeconds / 2});
+ Assert.equal(links.length, 3, "old bookmark returns as history");
+ Assert.equal(links[0].url, bookmarks[1].url, "still have newer bookmark");
+ Assert.equal(links[1].url, bookmarks[0].url, "old bookmark now is newer history");
+ Assert.equal(links[1].type, "history", "old bookmark now is history");
+ Assert.equal(links[2].url, testURI, "still have the history");
+
+ // Bookmark the history item
+ await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ title: "now a bookmark",
+ url: testURI
+ });
+
+ links = await provider.getHighlights();
+ Assert.equal(links.length, 3, "a visited bookmark doesn't appear as bookmark and history");
+ Assert.equal(links[0].url, testURI, "history is now the first, i.e., most recent, bookmark");
+ Assert.equal(links[0].type, "bookmark", "was history now bookmark");
+ Assert.equal(links[1].url, bookmarks[1].url, "still have younger bookmark now second");
+ Assert.equal(links[2].url, bookmarks[0].url, "still have older bookmark now third");
+});
+
add_task(async function getTopFrecentSites() {
await setUpActivityStreamTest();
let provider = NewTabUtils.activityStreamLinks;
let links = await provider.getTopSites({topsiteFrecency: 100});
Assert.equal(links.length, 0, "empty history yields empty links");
// add a visit