diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index ec5f911115..2efae9cbea 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -124,7 +124,9 @@ function defaultQuery(conditions = "") { h.visit_count, h.typed, h.id, t.open_count, h.frecency FROM moz_places h LEFT JOIN moz_favicons f ON f.id = h.favicon_id - LEFT JOIN moz_openpages_temp t ON t.url = h.url + LEFT JOIN moz_openpages_temp t + ON t.url = h.url + AND t.userContextId = :userContextId WHERE h.frecency <> 0 AND AUTOCOMPLETE_MATCH(:searchString, h.url, CASE WHEN bookmarked THEN @@ -148,6 +150,7 @@ const SQL_SWITCHTAB_QUERY = FROM moz_openpages_temp t LEFT JOIN moz_places h ON h.url_hash = hash(t.url) AND h.url = t.url WHERE h.id IS NULL + AND t.userContextId = :userContextId AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL, NULL, NULL, NULL, t.open_count, :matchBehavior, :searchBehavior) @@ -167,7 +170,9 @@ const SQL_ADAPTIVE_QUERY = ) AS i JOIN moz_places h ON h.id = i.place_id LEFT JOIN moz_favicons f ON f.id = h.favicon_id - LEFT JOIN moz_openpages_temp t ON t.url = h.url + LEFT JOIN moz_openpages_temp t + ON t.url = h.url + AND t.userContextId = :userContextId WHERE AUTOCOMPLETE_MATCH(NULL, h.url, IFNULL(btitle, h.title), tags, h.visit_count, h.typed, bookmarked, @@ -296,15 +301,17 @@ XPCOMUtils.defineLazyServiceGetter(this, "textURIService", XPCOMUtils.defineLazyGetter(this, "SwitchToTabStorage", () => Object.seal({ _conn: null, // Temporary queue used while the database connection is not available. - _queue: new Set(), + _queue: new Map(), initDatabase: Task.async(function* (conn) { // To reduce IO use an in-memory table for switch-to-tab tracking. // Note: this should be kept up-to-date with the definition in // nsPlacesTables.h. yield conn.execute( `CREATE TEMP TABLE moz_openpages_temp ( - url TEXT PRIMARY KEY, - open_count INTEGER + url TEXT, + userContextId INTEGER, + open_count INTEGER, + PRIMARY KEY (url, userContextId) )`); // Note: this should be kept up-to-date with the definition in @@ -315,44 +322,64 @@ XPCOMUtils.defineLazyGetter(this, "SwitchToTabStorage", () => Object.seal({ WHEN NEW.open_count = 0 BEGIN DELETE FROM moz_openpages_temp - WHERE url = NEW.url; + WHERE url = NEW.url + AND userContextId = NEW.userContextId; END`); this._conn = conn; // Populate the table with the current cache contents... - this._queue.forEach(this.add, this); + for (let [userContextId, uris] of this._queue) { + for (let uri of uris) { + this.add(uri, userContextId); + } + } // ...then clear it to avoid double additions. this._queue.clear(); }), - add: function (uri) { + add(uri, userContextId) { if (!this._conn) { - this._queue.add(uri); + if (!this._queue.has(userContextId)) { + this._queue.set(userContextId, new Set()); + } + this._queue.get(userContextId).add(uri); return; } this._conn.executeCached( - `INSERT OR REPLACE INTO moz_openpages_temp (url, open_count) - VALUES ( :url, IFNULL( (SELECT open_count + 1 - FROM moz_openpages_temp - WHERE url = :url), - 1 - ) + `INSERT OR REPLACE INTO moz_openpages_temp (url, userContextId, open_count) + VALUES ( :url, + :userContextId, + IFNULL( ( SELECT open_count + 1 + FROM moz_openpages_temp + WHERE url = :url + AND userContextId = :userContextId ), + 1 + ) )` - , { url: uri.spec }); + , { url: uri.spec, userContextId }); }, - delete: function (uri) { + delete(uri, userContextId) { if (!this._conn) { - this._queue.delete(uri); + // This should not happen. + if (!this._queue.has(userContextId)) { + throw new Error("Unknown userContextId!"); + } + + this._queue.get(userContextId).delete(uri); + if (this._queue.get(userContextId).size == 0) { + this._queue.delete(userContextId); + } return; } this._conn.executeCached( `UPDATE moz_openpages_temp SET open_count = open_count - 1 - WHERE url = :url` - , { url: uri.spec }); + WHERE url = :url + AND userContextId = :userContextId` + , { url: uri.spec, userContextId }); }, shutdown: function () { @@ -1754,6 +1781,7 @@ Search.prototype = { // We only want to search the tokens that we are left with - not the // original search string. searchString: this._searchTokens.join(" "), + userContextId: this._userContextId, // Limit the query to the the maximum number of desired results. // This way we can avoid doing more work than needed. maxResults: Prefs.maxRichResults @@ -1777,6 +1805,7 @@ Search.prototype = { // We only want to search the tokens that we are left with - not the // original search string. searchString: this._searchTokens.join(" "), + userContextId: this._userContextId, maxResults: Prefs.maxRichResults } ]; @@ -1796,7 +1825,8 @@ Search.prototype = { search_string: this._searchString, query_type: QUERYTYPE_FILTERED, matchBehavior: this._matchBehavior, - searchBehavior: this._behavior + searchBehavior: this._behavior, + userContextId: this._userContextId, } ]; }, @@ -1980,12 +2010,12 @@ UnifiedComplete.prototype = { // mozIPlacesAutoComplete - registerOpenPage: function PAC_registerOpenPage(uri) { - SwitchToTabStorage.add(uri); + registerOpenPage(uri, userContextId) { + SwitchToTabStorage.add(uri, userContextId); }, - unregisterOpenPage: function PAC_unregisterOpenPage(uri) { - SwitchToTabStorage.delete(uri); + unregisterOpenPage(uri, userContextId) { + SwitchToTabStorage.delete(uri, userContextId); }, // nsIAutoCompleteSearch diff --git a/toolkit/components/places/mozIPlacesAutoComplete.idl b/toolkit/components/places/mozIPlacesAutoComplete.idl index 6ff82e6674..7f3247fdc3 100644 --- a/toolkit/components/places/mozIPlacesAutoComplete.idl +++ b/toolkit/components/places/mozIPlacesAutoComplete.idl @@ -116,8 +116,10 @@ interface mozIPlacesAutoComplete : nsISupports * * @param aURI * The URI to register as an open page. + * @param aUserContextId + * The Container Id of the tab. */ - void registerOpenPage(in nsIURI aURI); + void registerOpenPage(in nsIURI aURI, in uint32_t aUserContextId); /** * Mark a page as no longer being open (either by closing the window or tab, @@ -129,6 +131,8 @@ interface mozIPlacesAutoComplete : nsISupports * * @param aURI * The URI to unregister as an open page. + * @param aUserContextId + * The Container Id of the tab. */ - void unregisterOpenPage(in nsIURI aURI); + void unregisterOpenPage(in nsIURI aURI, in uint32_t aUserContextId); }; diff --git a/toolkit/components/places/nsPlacesTables.h b/toolkit/components/places/nsPlacesTables.h index 0b6e414fbc..aca92735e9 100644 --- a/toolkit/components/places/nsPlacesTables.h +++ b/toolkit/components/places/nsPlacesTables.h @@ -133,8 +133,10 @@ // nsPlacesAutoComplete.js. #define CREATE_MOZ_OPENPAGES_TEMP NS_LITERAL_CSTRING( \ "CREATE TEMP TABLE moz_openpages_temp (" \ - " url TEXT PRIMARY KEY" \ + " url TEXT" \ + ", userContextId INTEGER" \ ", open_count INTEGER" \ + ", PRIMARY KEY (url, userContextId)" \ ")" \ ) diff --git a/toolkit/components/places/nsPlacesTriggers.h b/toolkit/components/places/nsPlacesTriggers.h index 37871a3eb5..d5b45ff5ec 100644 --- a/toolkit/components/places/nsPlacesTriggers.h +++ b/toolkit/components/places/nsPlacesTriggers.h @@ -192,7 +192,8 @@ "WHEN NEW.open_count = 0 " \ "BEGIN " \ "DELETE FROM moz_openpages_temp " \ - "WHERE url = NEW.url;" \ + "WHERE url = NEW.url " \ + "AND userContextId = NEW.userContextId;" \ "END" \ ) diff --git a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js index bc252efe0b..11e917e187 100644 --- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js +++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js @@ -286,19 +286,19 @@ var addBookmark = Task.async(function* (aBookmarkObj) { } }); -function addOpenPages(aUri, aCount=1) { +function addOpenPages(aUri, aCount=1, aUserContextId=0) { let ac = Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"] .getService(Ci.mozIPlacesAutoComplete); for (let i = 0; i < aCount; i++) { - ac.registerOpenPage(aUri); + ac.registerOpenPage(aUri, aUserContextId); } } -function removeOpenPages(aUri, aCount=1) { +function removeOpenPages(aUri, aCount=1, aUserContextId=0) { let ac = Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"] .getService(Ci.mozIPlacesAutoComplete); for (let i = 0; i < aCount; i++) { - ac.unregisterOpenPage(aUri); + ac.unregisterOpenPage(aUri, aUserContextId); } } diff --git a/toolkit/components/places/tests/unifiedcomplete/test_tab_matches.js b/toolkit/components/places/tests/unifiedcomplete/test_tab_matches.js index 646519c322..740b8d8edd 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_tab_matches.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_tab_matches.js @@ -11,9 +11,11 @@ add_task(function* test_tab_matches() { let uri2 = NetUtil.newURI("http://xyz.net/"); let uri3 = NetUtil.newURI("about:mozilla"); let uri4 = NetUtil.newURI("data:text/html,test"); + let uri5 = NetUtil.newURI("http://foobar.org"); yield PlacesTestUtils.addVisits([ { uri: uri1, title: "ABC rocks" }, - { uri: uri2, title: "xyz.net - we're better than ABC" } + { uri: uri2, title: "xyz.net - we're better than ABC" }, + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ" } ]); addOpenPages(uri1, 1); // Pages that cannot be registered in history. @@ -35,7 +37,8 @@ add_task(function* test_tab_matches() { searchParam: "enable-actions", matches: [ makeSearchMatch("abc", { heuristic: true }), makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), - { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ] + { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] }, + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] }); do_print("three results, both normal results are tab matches"); @@ -45,7 +48,39 @@ add_task(function* test_tab_matches() { searchParam: "enable-actions", matches: [ makeSearchMatch("abc", { heuristic: true }), makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), - makeSwitchToTabMatch("http://xyz.net/", { title: "xyz.net - we're better than ABC" }) ] + makeSwitchToTabMatch("http://xyz.net/", { title: "xyz.net - we're better than ABC" }), + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] + }); + + do_print("a container tab is not visible in 'switch to tab'"); + addOpenPages(uri5, 1, /* userContextId: */ 3); + yield check_autocomplete({ + search: "abc", + searchParam: "enable-actions", + matches: [ makeSearchMatch("abc", { heuristic: true }), + makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), + makeSwitchToTabMatch("http://xyz.net/", { title: "xyz.net - we're better than ABC" }), + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] + }); + + do_print("a container tab should not see 'switch to tab' for other container tabs"); + yield check_autocomplete({ + search: "abc", + searchParam: "enable-actions user-context-id:3", + matches: [ makeSearchMatch("abc", { heuristic: true }), + makeSwitchToTabMatch("http://foobar.org/", { title: "foobar.org - much better than ABC, definitely better than XYZ" }), + { uri: uri1, title: "ABC rocks", style: [ "favicon" ] }, + { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ] + }); + + do_print("a different container tab should not see any 'switch to tab'"); + yield check_autocomplete({ + search: "abc", + searchParam: "enable-actions user-context-id:2", + matches: [ makeSearchMatch("abc", { heuristic: true }), + { uri: uri1, title: "ABC rocks", style: [ "favicon" ] }, + { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] }, + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] }); do_print("three results, both normal results are tab matches, one has multiple tabs"); @@ -55,7 +90,8 @@ add_task(function* test_tab_matches() { searchParam: "enable-actions", matches: [ makeSearchMatch("abc", { heuristic: true }), makeSwitchToTabMatch("http://abc.com/", { title: "ABC rocks" }), - makeSwitchToTabMatch("http://xyz.net/", { title: "xyz.net - we're better than ABC" }) ] + makeSwitchToTabMatch("http://xyz.net/", { title: "xyz.net - we're better than ABC" }), + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] }); do_print("three results, no tab matches (disable-private-actions)"); @@ -64,7 +100,8 @@ add_task(function* test_tab_matches() { searchParam: "enable-actions disable-private-actions", matches: [ makeSearchMatch("abc", { heuristic: true }), { uri: uri1, title: "ABC rocks", style: [ "favicon" ] }, - { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ] + { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] }, + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] }); do_print("two results (actions disabled)"); @@ -72,7 +109,8 @@ add_task(function* test_tab_matches() { search: "abc", searchParam: "", matches: [ { uri: uri1, title: "ABC rocks", style: [ "favicon" ] }, - { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ] + { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] }, + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] }); do_print("three results, no tab matches"); @@ -83,7 +121,8 @@ add_task(function* test_tab_matches() { searchParam: "enable-actions", matches: [ makeSearchMatch("abc", { heuristic: true }), { uri: uri1, title: "ABC rocks", style: [ "favicon" ] }, - { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] } ] + { uri: uri2, title: "xyz.net - we're better than ABC", style: [ "favicon" ] }, + { uri: uri5, title: "foobar.org - much better than ABC, definitely better than XYZ", style: [ "favicon" ] } ] }); do_print("tab match search with restriction character");