home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 March / Chip_2011.03_CD.iso / Tools / yabar.msi / fil7E8B1D5B55761F0C097FFD1EAAA47942 < prev    next >
Encoding:
Text File  |  2010-04-16  |  20.9 KB  |  782 lines

  1. var gYaFTab = {
  2.   _compatibilityMode: null,
  3.   
  4.   get compatibilityMode() {
  5.     if (this._compatibilityMode === null) {
  6.       // ftab.compatibility pref:
  7.       //   0 - always false
  8.       //   1 - depends (default)
  9.       //   2 - always true
  10.       let compatibilityMode = gYaSearchService.getIntPref("yasearch.general.ftab.compatibility");
  11.       
  12.       switch (compatibilityMode) {
  13.         case 1:
  14.           const COMPATIBILITY_LIST = ["{c45c406e-ab73-11d8-be73-000a95be3b12}",//webdeveloper
  15.                                       "{dc572301-7619-498c-a57d-39143191b318}"//tmp
  16.                                      ];
  17.           
  18.           const ExtensionManager = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
  19.           const PREFIX_ITEM_URI = "urn:mozilla:item:";
  20.           const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
  21.           const RdfService = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService)
  22.           const nsIRDFLiteral = Ci.nsIRDFLiteral;
  23.           
  24.           function isExtEnabled(aExtID) {
  25.             let enabled = true;
  26.             let itemResource = RdfService.GetResource(PREFIX_ITEM_URI + aExtID);
  27.             if (itemResource) {
  28.               let ds = ExtensionManager.datasource;
  29.               let target = ds.GetTarget(itemResource, RdfService.GetResource(PREFIX_NS_EM + "isDisabled"), true);
  30.               if (target && target instanceof nsIRDFLiteral)
  31.                 enabled = (target.Value != "true");
  32.             }
  33.             return enabled;
  34.           }
  35.           
  36.           let extItems = ExtensionManager.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION, {});
  37.           this._compatibilityMode = !!extItems.some(function(item) {
  38.             return COMPATIBILITY_LIST.indexOf(item.id) > -1 && isExtEnabled(item.id);
  39.           });
  40.           
  41.           break;
  42.         
  43.         case 2:
  44.           this._compatibilityMode = true;
  45.           break;
  46.         
  47.         case 0:
  48.         default:
  49.           this._compatibilityMode = false;
  50.           break;
  51.       }
  52.     }
  53.     
  54.     return this._compatibilityMode;
  55.   },
  56.   
  57.   _init: function() {
  58.     OBSERVER_SERVICE.addObserver(this, "quit-application", false);
  59.     this.ProtocolHandler.addHandler(this.barProtocolHandler);
  60.   },
  61.   
  62.   _uninit: function() {
  63.     this.ProtocolHandler.removeHandler(this.barProtocolHandler);
  64.     OBSERVER_SERVICE.removeObserver(this, "quit-application");
  65.     this._stop();
  66.   },
  67.   
  68.   observe: function(aSubject, aTopic, aData) {
  69.     if (aTopic === "quit-application") {
  70.       this._uninit();
  71.     }
  72.   },
  73.   
  74.   barProtocolHandler: {
  75.     canHandleSpec: function(aSpec) {
  76.       return (aSpec && aSpec.toLowerCase() === "tabs");
  77.     },
  78.     
  79.     newURI: function(aSpec, aOriginalCharset, aBaseURI) {
  80.       if (!this.canHandleSpec(aSpec))
  81.         return null;
  82.       
  83.       return "chrome://yasearch/content/ftab/ftab.xul";
  84.     },
  85.  
  86.     newChannel: function(aURI) {
  87.       return null;
  88.     }
  89.   },
  90.   
  91.   // exp
  92.   get settings() {
  93.     delete this.settings;
  94.     
  95.     Cu.import("resource://yasearch/JSON.jsm");
  96.     
  97.     const kFactor = 0.75;
  98.     const kMaxRows = 8;
  99.     const kMaxCols = 8;
  100.     
  101.     let thumbsInRow = 3,
  102.         thumbsInCol = 3;
  103.     
  104.     let settingsStr = gYaSearchService.getCharPref("yasearch.general.ftab.settings");//{"rows":3,"cols":3}
  105.     
  106.     if (settingsStr) {
  107.       let settings;
  108.       try {
  109.         settings = JSON.parse(settingsStr);
  110.       } catch(e) {}
  111.       
  112.       if (settings && settings.rows && settings.cols) {
  113.         thumbsInRow = Math.min(kMaxCols, Math.max(2, parseInt(settings.cols, 10)));
  114.         thumbsInCol = Math.min(kMaxRows, Math.max(2, parseInt(settings.rows, 10)));
  115.       }
  116.     }
  117.     
  118.     return this.settings = {
  119.       MAX_THUMBS: kMaxRows * kMaxCols,
  120.       thumbsInRow: thumbsInRow,
  121.       thumbsInCol: thumbsInCol,
  122.       thumbsNmb: thumbsInRow * thumbsInCol,
  123.       screenFactor: kFactor * (thumbsInCol / thumbsInRow)
  124.     };
  125.   },
  126.   
  127.   /**
  128.    * pages data
  129.    *
  130.    **/
  131.   PAGES_DATA_VERSION: 1,
  132.   
  133.   _pagesData: null,
  134.   
  135.   get _pagesDataFile() {
  136.     delete this._pagesDataFile;
  137.     
  138.     let fileName = "ftab.data.xml";
  139.     
  140.     let file = gYaSearchService.getYandexDir();
  141.     file.append(fileName);
  142.     
  143.     if (!file.exists() || !file.isFile()) {
  144.       try {
  145.         file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0755);
  146.       } catch(e) {}
  147.     }
  148.     
  149.     return this._pagesDataFile = (file.exists() && file.isFile() && file.isReadable()) ? file : null;
  150.   },
  151.   
  152.   _loadPagesDataXML: function() {
  153.     let dataFile = this._pagesDataFile;
  154.     let data = dataFile ? gYaSearchService.readFile(dataFile) : "";
  155.     
  156.     let userData = gYaSearchService.safeE4Xml(data,
  157.                    '<data version="' + this.PAGES_DATA_VERSION + '"><pages/></data>', 'data');
  158.     
  159.     //let version = parseInt(userData.@version,10);
  160.     //if (version > 0 && version < this.PAGES_DATA_VERSION) {
  161.     //  migrate
  162.     //}
  163.     
  164.     return userData;
  165.   },
  166.   
  167.   _stringToURI: function(aString) {
  168.     var uri = null;
  169.     var URIFixup = Components.classes["@mozilla.org/docshell/urifixup;1"]
  170.                              .getService(Components.interfaces.nsIURIFixup);
  171.     try {
  172.       uri = URIFixup.createFixupURI(aString, URIFixup.FIXUP_FLAG_NONE);
  173.     } catch(e) {}
  174.     
  175.     return uri;
  176.   },
  177.   
  178.   getURLFromString: function(aString) {
  179.     let uri = this._stringToURI(aString);
  180.     
  181.     if (uri && uri.spec && (uri.scheme == "file" || (uri.host && /^https?$/.test(uri.scheme))))
  182.       return uri.spec.split("#")[0];
  183.     
  184.     return null;
  185.   },
  186.   
  187.   _loadPagesData: function() {
  188.     if (this._pagesData)
  189.       return;
  190.     
  191.     let pagesData = this._loadPagesDataXML();
  192.     
  193.     //[2do] batch get data & update
  194.     for each (let page in pagesData.pages.page) {
  195.       page.@status = "restore";
  196.       
  197.       let url = this.getURLFromString(page.@url.toString());
  198.       
  199.       if (!url)
  200.         delete page.@url;
  201.       
  202.       page.@index = parseInt(page.@index,10);
  203.     }
  204.     
  205.     let maxIndex = this.settings.MAX_THUMBS;
  206.     let notValidPages = pagesData.pages.page.(function::attribute("url").toString() == "" ||
  207.                                               function::attribute("index") > maxIndex ||
  208.                                               function::attribute("index") < 1);
  209.     while (notValidPages[0])
  210.       delete notValidPages[0];
  211.     
  212.     this._pagesData = pagesData;
  213.     
  214.     let thumbsNmb = this.settings.thumbsNmb;
  215.     
  216.     for each (let page in pagesData.pages.page) {
  217.       if (page.@index <= thumbsNmb)
  218.         this.getURLDataForPage(page);
  219.     }
  220.   },
  221.   
  222.   __savePagesDataTimer: null,
  223.   
  224.   _savePagesDataTimed: function() {
  225.     if (this.__savePagesDataTimer)
  226.       this.__savePagesDataTimer.cancel();
  227.     
  228.     let me = this;
  229.     this.__savePagesDataTimer = new G_Timer(function() {
  230.       me._savePagesData();
  231.     }, 10 * 1000);
  232.   },
  233.   
  234.   _savePagesData: function() {
  235.     if (!this._pagesData)
  236.       return;
  237.     
  238.     //delete some attributes
  239.     let data = <data version={this.PAGES_DATA_VERSION}><pages/></data>;
  240.     
  241.     for each (let page in this._pagesData.pages.page) {
  242.       let _page = <page url={page.@url} index={page.@index}/>
  243.       
  244.       for each (var aAttrName in ["custom_title"]) {
  245.         if (page.attribute(aAttrName).length()) {
  246.           _page.@[aAttrName] = page.@[aAttrName].toString();
  247.         }
  248.       }
  249.       
  250.       data.pages.page += _page;
  251.     }
  252.     
  253.     let dataFile = this._pagesDataFile;
  254.     if (dataFile)
  255.       gYaSearchService.writeFile(dataFile, data);
  256.   },
  257.   
  258.   _destroyPagesData: function() {
  259.     this._savePagesData();
  260.     this.__pagesDataDOM = null;
  261.     this.__pagesDataDOMCloned = null;
  262.     this._pagesData = null;
  263.   },
  264.   
  265.   _getPageByIndex: function(aPageIndex) {
  266.     let pages = this._getPagesByAttribute("index", aPageIndex);
  267.     return pages && pages.length() ? pages[0] : null;
  268.   },
  269.   
  270.   _getPagesByAttribute: function(aAttrName, aAttrValue) {
  271.     if (!this._pagesData)
  272.       return;
  273.     
  274.     return this._pagesData.pages.page.(function::attribute(aAttrName) == aAttrValue);
  275.   },
  276.   
  277.   _deletePageByIndex: function(aPageIndex) {
  278.     let existedPages = this._getPagesByAttribute("index", aPageIndex);
  279.     if (!existedPages.length())
  280.       return;
  281.     
  282.     while (existedPages[0])
  283.       delete existedPages[0];
  284.     
  285.     return true;
  286.   },
  287.   
  288.   addPage: function(aPageIndex, aURL, aTitle, aRefreshConditions) {
  289.     if (!this._pagesData)
  290.       return;
  291.     
  292.     this._deletePageByIndex(aPageIndex);
  293.     
  294.     let page = <page url={aURL} index={aPageIndex}/>;
  295.     if (typeof aTitle === "string")
  296.       page.@custom_title = aTitle;
  297.     
  298.     this.getURLDataForPage(page, aRefreshConditions);
  299.     this._pagesData.pages.page += page;
  300.     
  301.     this.__pagesDataDOM = null;
  302.     this.notifyTabClients("UPDATE_PROPS", {pageIndex: aPageIndex, url: aURL});
  303.   },
  304.   
  305.   removePage: function(aPageIndex) {
  306.     if (!this._pagesData)
  307.       return;
  308.     
  309.     if (this._deletePageByIndex(aPageIndex)) {
  310.       this.__pagesDataDOM = null;
  311.       this.notifyTabClients("UPDATE_PROPS", {pageIndex: aPageIndex});
  312.     }
  313.   },
  314.   
  315.   swapPages: function(aPageIndexFrom, aPageIndexTo) {
  316.     if (!(aPageIndexFrom && aPageIndexTo))
  317.       return;
  318.     
  319.     let fromPage = this._getPageByIndex(aPageIndexFrom);
  320.     let toPage = this._getPageByIndex(aPageIndexTo);
  321.     
  322.     if (fromPage)
  323.       fromPage.@index = aPageIndexTo;
  324.     
  325.     if (toPage)
  326.       toPage.@index = aPageIndexFrom;
  327.     
  328.     this.__pagesDataDOM = null;
  329.     this.notifyTabClients("UPDATE_PROPS", {pageIndex: aPageIndexFrom});
  330.     this.notifyTabClients("UPDATE_PROPS", {pageIndex: aPageIndexTo});
  331.   },
  332.   
  333.   updatePageProps: function(aData) {
  334.     if (!aData || !this._pagesData)
  335.       return;
  336.     
  337.     let url = aData.url;
  338.     
  339.     for each (let page in this._getPagesByAttribute("url", url))
  340.       for (let [propName, propValue] in Iterator(aData))
  341.         page.@[propName] = propValue || "";
  342.     
  343.     this.__pagesDataDOM = null;
  344.     this.notifyTabClients("UPDATE_PROPS", {url: url});
  345.   },
  346.   
  347.   setPageProps: function(aPageIndex, aData, aRefreshConditions) {
  348.     if (!this._pagesData)
  349.       return;
  350.     
  351.     if (("title" in aData) && !("custom_title" in aData)) {
  352.       aData.custom_title = aData.title;
  353.       aData.title = "";
  354.     }
  355.     
  356.     let safeUnicode = gYaSearchService.safeUnicode;
  357.     for (let [propName, propValue] in Iterator(aData))
  358.       aData[propName] = safeUnicode(propValue);
  359.     
  360.     aPageIndex = aPageIndex.toString();
  361.     
  362.     let page = this._getPageByIndex(aPageIndex);
  363.     
  364.     if (!page)
  365.       return this.addPage(aPageIndex, aData.url, aData.custom_title, aRefreshConditions);
  366.     
  367.     let prevURL = page.@url.toString();
  368.     
  369.     for (let [propName, propValue] in Iterator(aData))
  370.       page.@[propName] = propValue || "";
  371.     
  372.     this.getURLDataForPage(page, aRefreshConditions);
  373.     
  374.     this.__pagesDataDOM = null;
  375.     this.notifyTabClients("UPDATE_PROPS", {pageIndex: aPageIndex, url: aData.url || null});
  376.   },
  377.   
  378.   getPageProps: function(aPageIndex) {
  379.     if (!this._pagesData)
  380.       return;
  381.     
  382.     aPageIndex = aPageIndex.toString();
  383.     
  384.     let result;
  385.     let page = this._getPageByIndex(aPageIndex);
  386.     
  387.     if (page) {
  388.       result = {
  389.         index: aPageIndex,
  390.         title: (page.@custom_title.toString() || page.@title.toString()),
  391.         url: page.@url.toString(),
  392.         state: page.@state.toString()
  393.       };
  394.       
  395.       result.img = this.getAboutImgURL(result.url);
  396.       
  397.     } else {
  398.       result = {
  399.         index: aPageIndex,
  400.         title: "",
  401.         url: "",
  402.         img: "",
  403.         state: ""
  404.       };
  405.     }
  406.     
  407.     return result;
  408.   },
  409.   
  410.   getPropsForURL: function(aURL) {
  411.     let page = <page url={aURL}/>;
  412.     this.getURLDataForPage(page);
  413.     
  414.     let props = {};
  415.     ["url", "title", "img", "state"].forEach(function(attr) {
  416.       props[attr] = page.@[attr].toString() || "";
  417.     });
  418.     
  419.     return props;
  420.   },
  421.   
  422.   /**
  423.    * DOM
  424.    **/
  425.   getImageForURL: function(aURL) {
  426.     return gYaStorage.getImageForURL(aURL);
  427.   },
  428.   
  429.   get secURLParam() {
  430.     delete this.secURLParam;
  431.     return this.secURLParam = (gYaSearchService.generateGUID.toString()).replace(/\{|\}|\-/g, "");
  432.   },
  433.   
  434.   __pagesDataDOM: null,
  435.   __pagesDataDOMCloned: null,
  436.   
  437.   get _pagesDataDOM() {
  438.     if (!this.__pagesDataDOM) {
  439.       this.__pagesDataDOMCloned = null;
  440.       
  441.       this.__pagesDataDOM = gYaSearchService.getDOMDocContent2("ftab/xsl-thumbs-template.xsl",
  442.                                 gYaSearchService.domParser.parseFromString(this._pagesData.toSource(), "text/xml"),
  443.                                 { secURLParam: this.secURLParam,
  444.                                   thumbsInRow: this.settings.thumbsInRow,
  445.                                   thumbsInCol: this.settings.thumbsInCol });
  446.     }
  447.     
  448.     var me = this;
  449.     new G_Timer(function() {
  450.       me.__pagesDataDOMCloned = me.__pagesDataDOM ? me.__pagesDataDOM.cloneNode(true) : null;
  451.     }, 2000);
  452.     
  453.     let res = this.__pagesDataDOMCloned || this.__pagesDataDOM.cloneNode(true);
  454.     this.__pagesDataDOMCloned = null;
  455.     
  456.     return res;
  457.   },
  458.   
  459.   /**
  460.    * Cleanup old sshots
  461.    **/
  462.   _cleanUpTimer: null,
  463.   
  464.   _cleanUp: function() {
  465.     if (!this._pagesData)
  466.       return;
  467.     
  468.     let urls = [];
  469.     for each (let page in this._pagesData.pages.page) {
  470.       let url = page.@url.toString();
  471.       if (url && urls.indexOf(url) === -1) {
  472.         urls.push(url);
  473.       }
  474.     }
  475.     
  476.     gYaStorage._dbCleanupOldURLData(urls);
  477.   },
  478.   
  479.   _isRunned: false,
  480.   
  481.   _run: function() {
  482.     if (this._isRunned)
  483.       return;
  484.     
  485.     this._loadPagesData();
  486.     
  487.     var me = this;
  488.     this._cleanUpTimer = new G_Timer(function() { me._cleanUp(); }, 60*60*1000, true);//1 hour
  489.     
  490.     this._isRunned = true;
  491.   },
  492.   
  493.   _stop: function() {
  494.     if (!this._isRunned)
  495.       return;
  496.     
  497.     if (this._SShotGrabber) {
  498.       this._SShotGrabber.destroy();
  499.       this._SShotGrabber = null;
  500.     }
  501.     
  502.     this._tabClients = [];
  503.     this._destroyPagesData();
  504.     
  505.     if (this._cleanUpTimer) {
  506.       this._cleanUpTimer.cancel();
  507.       this._cleanUpTimer = null;
  508.     }
  509.     
  510.     this._isRunned = false;
  511.   },
  512.   
  513.   _checkCanStop: function() {
  514.   },
  515.   
  516.   /**
  517.    * Clients
  518.    **/
  519.   _tabClients: [],
  520.   
  521.   createTabClient: function(aYaFTabObject) {
  522.     if (this._tabClients.some(function(aTabClient) { return aTabClient === aYaFTabObject; }))
  523.       return;
  524.     
  525.     this._tabClients.push(aYaFTabObject);
  526.     
  527.     this._run();
  528.     
  529.     return this._pagesDataDOM;
  530.   },
  531.   
  532.   destroyTabClient: function(aYaFTabObject) {
  533.     this._tabClients = this._tabClients.filter(function(aTabClient) { return aTabClient !== aYaFTabObject; });
  534.     this._checkCanStop();
  535.   },
  536.   
  537.   notifyTabClients: function(aTopic, aData, aTabClient) {
  538.     (aTabClient ? [aTabClient] : this._tabClients)
  539.     .forEach(function(aTabClient) {
  540.       aTabClient.observe(aTopic, aData);
  541.     });
  542.     
  543.     if (aTopic == "UPDATE_PROPS") {
  544.       this._savePagesDataTimed();
  545.     }
  546.   },
  547.   
  548.   getURLData: function(aURL, aRefreshConditions) {
  549.     let url = this.getURLFromString(aURL);
  550.     if (!url)
  551.       return;
  552.     
  553.     let urlData = gYaStorage.getURLData(url);
  554.     
  555.     if (urlData && urlData.img)
  556.       urlData.img = this.getAboutImgURL(url);
  557.     
  558.     if (this._isScreenExpired(url, urlData, aRefreshConditions))
  559.       this.getSShotForURL(url);
  560.     
  561.     return urlData;
  562.   },
  563.   
  564.   getURLDataForPage: function(aPage, aRefreshConditions) {
  565.     let url = this.getURLFromString(aPage.@url.toString());
  566.     if (!url)
  567.       return;
  568.     
  569.     let urlData = this.getURLData(url, aRefreshConditions) || {};
  570.     
  571.     if (this.SShotGrabber.isURLInQueue(url))
  572.       urlData.state = "busy";
  573.     
  574.     for (let [propName, propValue] in Iterator(urlData))
  575.       aPage.@[propName] = propValue || "";
  576.     
  577.     return urlData;
  578.   },
  579.   
  580.   getURLsData: function(aURLs) {/*nyi*/},
  581.   
  582.   /**
  583.    * Screen grab
  584.    **/
  585.   _SShotGrabber: null,
  586.   get SShotGrabber() {
  587.     return this._SShotGrabber || (this._SShotGrabber = new YaSShotGrabber(this));
  588.   },
  589.   
  590.   _isScreenExpired: function(aURL, aURLData, aRefreshConditions) {
  591.     let url = this.getURLFromString(aURL);
  592.     if (!url)
  593.       return false;
  594.     
  595.     if (this.SShotGrabber.isURLInQueue(url))
  596.       return false;
  597.     
  598.     let urlData = (typeof aURLData === "undefined") ? this.getURLData(url) : aURLData;
  599.     if (!urlData)
  600.       return true;
  601.     
  602.     let checkTimeDiff = Math.abs(G_TIME_NOW - urlData.checkTime);
  603.     
  604.     if (aRefreshConditions) {
  605.       if ("forcedRefresh" in aRefreshConditions && aRefreshConditions.forcedRefresh === true)
  606.         return true;
  607.       
  608.       if ("checkedTimeMin" in aRefreshConditions && aRefreshConditions.checkedTimeMin > urlData.checkTime)
  609.         return true;
  610.       
  611.       if ("olderThan" in aRefreshConditions && checkTimeDiff > aRefreshConditions.olderThan)
  612.         return true;
  613.     }
  614.     
  615.     if ((checkTimeDiff > (7 * DAY_SECS)) || (urlData.httpStatus == 404 && checkTimeDiff > (3 * DAY_SECS))) {
  616.       return true;
  617.     }
  618.     
  619.     return false;
  620.   },
  621.   
  622.   getSShotForURL: function(aURL) {
  623.     if (this.SShotGrabber.getCanvasForURL(aURL)) {
  624.       this.updatePageProps({url: aURL, state: "busy"});
  625.     }
  626.   },
  627.   
  628.   getAboutImgURL: function(aURL) {
  629.     return aURL ? ("about:yandex-tabs?sec=" + this.secURLParam + "&image=" + aURL) : null;
  630.   },
  631.   
  632.   _removeImgURLFromCache: function(aURL) {
  633.     let uri = gYaSearchService.makeURI(aURL);
  634.     if (uri) {
  635.       try {
  636.         const imageCache = Cc["@mozilla.org/image/cache;1"].getService(Ci.imgICache);
  637.         imageCache.removeEntry(uri);
  638.       } catch(e) {}
  639.     }
  640.   },
  641.   
  642.   onSShotCreated: function(aPageData) {
  643.     gYaStorage.setURLData(aPageData);
  644.     
  645.     if (aPageData) {
  646.       let imgURL = this.getAboutImgURL(aPageData.url);
  647.       this._removeImgURLFromCache(imgURL);
  648.       aPageData.img = imgURL;
  649.     }
  650.     
  651.     this.updatePageProps(gYaStorage.getURLData(aPageData.url));
  652.   },
  653.   
  654.   getSitesFromHistory: function(aMaxResults, aOnComplete) {
  655.     if (!aOnComplete)
  656.       return;
  657.     
  658.     let me = this;
  659.     new G_Timer(function() {
  660.       me._getSitesFromHistory(aMaxResults, aOnComplete);
  661.     }, 0);
  662.   },
  663.   
  664.   _getSitesFromHistory: function(aMaxResults, aOnComplete) {
  665.     let maxResults = aMaxResults || 10;
  666.     
  667.     let historyService = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
  668.     
  669.     let query = historyService.getNewQuery();
  670.     
  671.     let options = historyService.getNewQueryOptions();
  672.     options.maxResults = maxResults * 2;
  673.     options.sortingMode = options.SORT_BY_DATE_DESCENDING;
  674.     
  675.     let result = (historyService.executeQuery(query, options)).root;
  676.     result.containerOpen = true;
  677.     
  678.     let faviconService = Cc["@mozilla.org/browser/favicon-service;1"].getService(Ci.nsIFaviconService);
  679.     let ioservice = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  680.     
  681.     const nsIURI = Ci.nsIURI;
  682.     
  683.     let sites = [];
  684.     //let uniqURLs = {};
  685.     for (let i = 0, count = result.childCount; i < count && i < maxResults; i++) {
  686.       let node = result.getChild(i);
  687.       
  688.       //let uniqURL = "_uniq_" + node.uri.split("#")[0];//&ncrnd for /\.ya\.ru$/.test(uri.host)
  689.       //if (uniqURLs[uniqURL] === true)
  690.       //  continue;
  691.       //uniqURLs[uniqURL] = true;
  692.       
  693.       let nodeURI = ioservice.newURI(node.uri, null, null);
  694.       
  695.       if (!/^https?|file$/.test(nodeURI.scheme))
  696.         continue;
  697.       
  698.       if (/\.swf$/.test(nodeURI.spec))
  699.         continue;
  700.       
  701.       let title = node.title;
  702.       let icon = node.icon ?
  703.                      (node.icon instanceof nsIURI ? node.icon.spec : node.icon) :
  704.                      faviconService.getFaviconImageForPage(nodeURI).spec;
  705.       
  706.       let pathForTitle = nodeURI.path.split("?")[0].split("/").pop();
  707.       
  708.       if (title == nodeURI.path || title == pathForTitle) {
  709.         if (!node.icon)
  710.           continue;
  711.         
  712.         title = nodeURI.spec;
  713.       }
  714.       
  715.       sites.push({
  716.         title: title,
  717.         url: node.uri,
  718.         icon: icon
  719.       });
  720.     }
  721.     
  722.     result.containerOpen = false;
  723.     
  724.     if (aOnComplete) {
  725.       aOnComplete(sites);
  726.       aOnComplete = null;
  727.     }
  728.   },
  729.   
  730.   findSites: function(aSearchString, aOnComplete) {
  731.     if (!aOnComplete)
  732.       return;
  733.     
  734.     let me = this;
  735.     new G_Timer(function() {
  736.       me._findSites(aSearchString, aOnComplete);
  737.     }, 0);
  738.   },
  739.   
  740.   _findSites: function(aSearchString, aOnComplete) {
  741.     let acomplete = Cc["@mozilla.org/autocomplete/search;1?name=history"]
  742.                         .getService(Ci.nsIAutoCompleteSearch);
  743.     
  744.     let callback = function(aResult) {
  745.       if (aOnComplete) {
  746.         aOnComplete(aResult);
  747.         aOnComplete = null;
  748.       }
  749.     };
  750.  
  751.     acomplete.startSearch(aSearchString, "", null, ({
  752.       onSearchResult: function(search, result) {
  753.         switch (result.searchResult) {
  754.           case result.RESULT_NOMATCH:
  755.             return callback();//break;
  756.           
  757.           case result.RESULT_SUCCESS:
  758.           case result.RESULT_SUCCESS_ONGOING:
  759.             acomplete.stopSearch();
  760.             
  761.             let sites = [];
  762.             for (let i = 0, count = result.matchCount; i < count; i++) {
  763.               sites.push({
  764.                 title: result.getCommentAt(i),
  765.                 url: result.getValueAt(i),
  766.                 icon: result.getImageAt(i)
  767.               });
  768.             }
  769.             callback(sites);
  770.             break;
  771.           
  772.           default:
  773.             break;
  774.         }
  775.       }
  776.     }));
  777.   }
  778. };
  779.  
  780. Components.utils.import("resource://yasearch/bar.protocol.jsm", gYaFTab);
  781.  
  782. gYaFTab._init();