"use strict";
const EXPORTED_SYMBOLS = ["DataProvider"];
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const {RESULT_SUCCESS: RESULT_SUCCESS, RESULT_NOMATCH: RESULT_NOMATCH} = Ci.nsIAutoCompleteResult;
const FIRST_PACKET_TIMEOUT = 200;
const NOMATCH_TIMEOUT = 400;
var DataProvider = {
PROVIDER_NAME: "offline-places-suggest",
DATA_SERVICE: "yaOmniBoxSuggestDataService",
_waitingTimer: null,
_searchNextStringTimer: null,
_searchingStrings: null,
_debugSearchStartTime: null,
_statData: {
time: 0},
init: function PlacesDataProvider_init(core) {
this.api = core.api;
Cu.import(this.api.Package.resolvePath("/native/urlengine.js"),this).URLEngine.init(this.api);
this.api.Settings.PrefsModule.set("browser.urlbar.default.behavior",0);
XPCOMUtils.defineLazyGetter(this,"_PlacesAutoComplete",function () {
return Components.classesByID["{d0272978-beab-4adc-a3d4-04b76acfa4e7}"].getService(Ci.nsIAutoCompleteSearch);
}
);
XPCOMUtils.defineLazyGetter(this,"_BookmarksService",function () {
return Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
}
);
this._providerService = this.api.Services.obtainService(this.api.componentID,this.DATA_SERVICE,this);
if (! this._providerService)
return;
if (! this._providerService.registerDataProvider(this))
return;
}
,
observeServiceEvent: function PlacesDataProvider_observeServiceEvent(aProviderID, aServiceName, aTopic, aData) {
if (this.DATA_SERVICE != aServiceName || this.api.componentID != aProviderID)
return;
switch (aTopic) {
case "start-collecting":
this._startSearch(aData);
break;
case "stop-collecting":
this._stopSearch();
break;
}

}
,
_startSearch: function PlacesDataProvider_startSearch(aSearchStrings) {
this._stopSearch();
this._originalSearchStrings = [].concat(aSearchStrings);
this._searchingStrings = [].concat(this._originalSearchStrings);
this._suggestions = [];
this._searchResult = RESULT_NOMATCH;
this._debugSearchStartTime = Date.now();
this._timing = {
};
this._searchNextString();
}
,
_searchNextString: function PlacesDataProvider__searchNextString() {
var isFirstSearch = 1 === this._originalSearchStrings.length - this._searchingStrings.length;
if (isFirstSearch && this._suggestions.length >= 5 || ! this._searchingStrings.length)
{
this.api.logger.trace("History (places) search time: " + (Date.now() - this._debugSearchStartTime) + " ms.");
this._PlacesAutoComplete.stopSearch();
this._finishSearch(true);
return;
}

if (isFirstSearch)
this._notifyResults();
var searchString = this._searchingStrings.shift();
this._timing[searchString] = Date.now();
this._searchNextStringTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._searchNextStringTimer.initWithCallback({
notify: (function () {
this._PlacesAutoComplete.startSearch(searchString,"enable-actions",null,this);
}
).bind(this)},0,Ci.nsITimer.TYPE_ONE_SHOT);
}
,
_stopSearch: function PlacesDataProvider_stopSearch() {
delete this._timing;
this._PlacesAutoComplete.stopSearch();
this._finishSearch(false);
}
,
_finishSearch: function PlacesDataProvider__finishSearch(aNotify) {
["_searchNextStringTimer"].forEach(function (timerName) {
var timer = this[timerName] || null;
if (timer)
{
timer.cancel();
delete this[timerName];
}

}
,this);
if (aNotify)
this._notifyResults();
delete this._originalSearchStrings;
delete this._searchingStrings;
delete this._suggestions;
}
,
_notifyResults: function PlacesDataProvider__notifyResults() {
this._providerService.handleResponse("start-collecting",{
originalSearchStrings: this._originalSearchStrings,
suggestions: this._suggestions,
stat: this._statData,
searchResult: RESULT_SUCCESS},this);
}
,
_getStyleForBookmark: function PlacesDataProvider__getStyleForBookmark(url, originalSearchString) {
try {
let uri = this.URLEngine.getURIFromString(url);
let keyword = this._BookmarksService.getKeywordForURI(uri);
return keyword.indexOf(originalSearchString) == 0 ? "keyword" : "bookmark";
}
catch (e) {

}

return "bookmark";
}
,
_processSearchResults: function PlacesDataProvider__processSearchResults(aAutoCompleteSearch, aAutoCompleteResult) {
var originalSearchString = aAutoCompleteResult.searchString;
var originalSearchStringLowerCase = originalSearchString.toLowerCase();
this._statData.time = Date.now() - this._timing[originalSearchString];
var urlsHash = Object.create(null);
this._suggestions.forEach(function (s) urlsHash[s.value] = true);
var titles = [];
var styles = [];
var images = [];
var i = - 1;
var suggestions = [];
while (++i < aAutoCompleteResult.matchCount) {
let title = aAutoCompleteResult.getCommentAt(i);
if (title == "404")
continue;
let url = this.URLEngine.validateURL(aAutoCompleteResult.getValueAt(i),originalSearchString);
if (/(clck\.yandex\.ru|yandex\.ru\/clck)\/redir\/|yabs\.yandex\.ru/i.test(url))
continue;
if (/^(www\.)?yandex\./.test(url) && /[?&]clid=/.test(url))
url = url.replace(/[?&]clid=[^&]+/,"").replace(/\/$/,"");
if (url in urlsHash)
continue;
let titleLowered = title.toLowerCase();
let noQueryURLLowered = url.split(/[?#]/)[0].toLowerCase();
let dropResult = ! ! originalSearchStringLowerCase && ! originalSearchStringLowerCase.split(/\s+/).filter(function (s) ! ! s).some(function (s) titleLowered.indexOf(s) !== - 1 || noQueryURLLowered.indexOf(s) !== - 1);
if (dropResult)
continue;
let style = aAutoCompleteResult.getStyleAt(i);
if (style == "keyword")
{
let spaceIndex = originalSearchString.indexOf(" ");
if (spaceIndex != - 1)
title += originalSearchString.substr(spaceIndex,originalSearchString.length);
}
 else
if (style == "bookmark")
{
style = this._getStyleForBookmark(url,originalSearchString);
}

let searchTextFromURL = this.URLEngine.extractSearchParam(url);
if (searchTextFromURL)
{
if (searchTextFromURL.toLowerCase().indexOf(originalSearchStringLowerCase) == - 1)
continue;
url = searchTextFromURL;
title = "";
style = "yaOfflineSearchHistory";
}

urlsHash[url] = true;
titles.push(title);
styles.push(style);
images.push("");
let httpSchemeRE = /^(https?:\/\/)/;
if (! searchTextFromURL && ! httpSchemeRE.test(url) && httpSchemeRE.test(aAutoCompleteResult.getValueAt(i)))
url = RegExp.$1 + url;
suggestions.push({
value: url,
comment: title,
image: aAutoCompleteResult.getImageAt(i),
style: style});
}

this._suggestions = this._suggestions.concat(suggestions);
this._searchResult = aAutoCompleteResult.searchResult;
if (this._searchResult == RESULT_NOMATCH || this._searchResult == RESULT_SUCCESS)
this._searchNextString(); else
this._notifyResults();
}
,
onSearchResult: function PlacesDataProvider_onSearchResult(aAutoCompleteSearch, aAutoCompleteResult) {
if (! this._suggestions)
{
this._finishSearch(false);
return;
}

this._statData.time = Date.now() - this._timing[aAutoCompleteResult.searchString];
this._processSearchResults(aAutoCompleteSearch,aAutoCompleteResult);
}
,
onUpdateSearchResult: function PlacesDataProvider_onUpdateSearchResult(aAutoCompleteSearch, aAutoCompleteResult) {

}
};
