1 # ***** BEGIN LICENSE BLOCK *****
2 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 # The contents of this file are subject to the Mozilla Public License Version
5 # 1.1 (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
7 # http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS IS" basis,
10 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 # for the specific language governing rights and limitations under the
14 # The Original Code is Microsummarizer.
16 # The Initial Developer of the Original Code is Mozilla.
17 # Portions created by the Initial Developer are Copyright (C) 2006
18 # the Initial Developer. All Rights Reserved.
21 # Myk Melez <myk@mozilla.org> (Original Author)
22 # Simon Bünzli <zeniko@gmail.com>
23 # Asaf Romano <mano@mozilla.com>
24 # Dan Mills <thunder@mozilla.com>
25 # Ryan Flint <rflint@dslr.net>
26 # Dietrich Ayala <dietrich@mozilla.com>
28 # Alternatively, the contents of this file may be used under the terms of
29 # either the GNU General Public License Version 2 or later (the "GPL"), or
30 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 # in which case the provisions of the GPL or the LGPL are applicable instead
32 # of those above. If you wish to allow use of your version of this file only
33 # under the terms of either the GPL or the LGPL, and not to allow others to
34 # use your version of this file under the terms of the MPL, indicate your
35 # decision by deleting the provisions above and replace them with the notice
36 # and other provisions required by the GPL or the LGPL. If you do not delete
37 # the provisions above, a recipient may use your version of this file under
38 # the terms of any one of the MPL, the GPL or the LGPL.
40 # ***** END LICENSE BLOCK *****
42 const Cc = Components.classes;
43 const Ci = Components.interfaces;
44 const Cr = Components.results;
45 const Cu = Components.utils;
60 const MICSUM_NS =
"http://www.mozilla.org/microsummaries/0.1";
61 const XSLT_NS =
"http://www.w3.org/1999/XSL/Transform";
70 Cu.import(
"resource://gre/modules/XPCOMUtils.jsm");
73 this.
_obs.addObserver(
this,
"xpcom-shutdown",
true);
74 this._ans.addObserver(
this,
false);
76 Cc[
"@mozilla.org/preferences-service;1"].
78 getBranch(
"browser.microsummary.").
83 this._cacheLocalGenerators();
89 var svc =
Cc[
"@mozilla.org/browser/nav-bookmarks-service;1"].
97 var svc =
Cc[
"@mozilla.org/browser/annotation-service;1"].
105 var svc =
Cc[
"@mozilla.org/network/io-service;1"].
113 var svc =
Cc[
"@mozilla.org/observer-service;1"].
125 _uri:
function MSS__uri(spec) {
126 return this._ios.newURI(spec,
null,
null);
133 this.__dirs =
Cc[
"@mozilla.org/file/directory_service;1"].
139 get _updateInterval() {
141 getPref(
"browser.microsummary.updateInterval", 30);
143 return Math.max(updateInterval, 1) * 60 * 1000;
148 _localGenerators: {},
155 contractID:
"@mozilla.org/microsummary/service;1",
156 classID: Components.ID(
"{460a9792-b154-4f26-a922-0f653e2c8f91}"),
158 value:
"@mozilla.org/microsummary/service;1," +
159 "getService,microsummary-generator-update-timer," +
160 "browser.microsummary.generatorUpdateInterval," +
164 Ci.nsISupportsWeakReference,
165 Ci.nsIAnnotationObserver,
171 case "xpcom-shutdown":
174 case "nsPref:changed":
175 if (
data ==
"enabled")
182 notify:
function MSS_notify(timer) {
183 this._updateGenerators();
186 _initTimers:
function MSS__initTimers() {
190 if (!
getPref(
"browser.microsummary.enabled",
true))
194 this.
_timer =
Cc[
"@mozilla.org/timer;1"].createInstance(
Ci.nsITimer);
197 notify:
function(timer) { this._svc._updateMicrosummaries() }
199 this.
_timer.initWithCallback(callback,
201 this.
_timer.TYPE_REPEATING_SLACK);
204 _destroy:
function MSS__destroy() {
205 this.
_obs.removeObserver(
this,
"xpcom-shutdown",
true);
206 this._ans.removeObserver(
this);
211 _updateMicrosummaries:
function MSS__updateMicrosummaries() {
212 var bookmarks = this._bookmarks;
214 var
now = Date.now();
215 var updateInterval = this._updateInterval;
216 for ( var
i = 0;
i < bookmarks.length;
i++ ) {
217 var bookmarkID = bookmarks[
i];
231 this.refreshMicrosummary(bookmarkID);
239 _updateGenerators:
function MSS__updateGenerators() {
240 var generators = this._localGenerators;
241 var update =
getPref(
"browser.microsummary.updateGenerators",
true);
242 if (!generators || !update)
245 for (let
uri in generators)
246 generators[
uri].update();
249 _updateMicrosummary:
function MSS__updateMicrosummary(bookmarkID, microsummary) {
250 var title = this._bms.getItemTitle(bookmarkID);
257 var bookmarkIdentity = bookmarkID;
260 if (!title || title != microsummary.content) {
261 this._bms.setItemTitle(bookmarkID, microsummary.content);
263 LOG(
"updated live title for " + bookmarkIdentity +
264 " from '" + (title ==
null ?
"<no live title>" : title) +
265 "' to '" + microsummary.content +
"'");
266 this.
_obs.notifyObservers(subject,
"microsummary-livetitle-updated", title);
269 LOG(
"didn't update live title for " + bookmarkIdentity +
"; it hasn't changed");
277 Date.now() + (microsummary.updateInterval || this._updateInterval));
284 _cacheLocalGenerators:
function MSS__cacheLocalGenerators() {
286 var appDir = this._dirs.get(
"MicsumGens",
Ci.nsIFile);
288 this._cacheLocalGeneratorDir(appDir);
291 var
profileDir = this._dirs.get(
"UsrMicsumGens",
Ci.nsIFile);
292 if (profileDir.exists())
293 this._cacheLocalGeneratorDir(profileDir);
303 _cacheLocalGeneratorDir:
function MSS__cacheLocalGeneratorDir(
dir) {
304 var files =
dir.directoryEntries.QueryInterface(
Ci.nsIDirectoryEnumerator);
305 var
file = files.nextFile;
310 if (file.isDirectory())
311 this._cacheLocalGeneratorDir(file);
313 this._cacheLocalGeneratorFile(file);
315 file = files.nextFile;
327 _cacheLocalGeneratorFile:
function MSS__cacheLocalGeneratorFile(
file) {
328 var
uri = this._ios.newFileURI(
file);
332 function MSS_cacheLocalGeneratorCallback(resource) {
333 try { t._handleLocalGenerator(resource) }
334 finally { resource.destroy() }
338 resource.load(callback);
341 _handleLocalGenerator:
function MSS__handleLocalGenerator(resource) {
343 throw(resource.uri.spec +
" microsummary generator loaded, but not XML");
346 generator.initFromXML(resource.content);
352 this._localGenerators[generator.uri.spec.split().join()] = generator;
354 LOG(
"loaded local microsummary generator\n" +
355 " file: " + generator.localURI.spec +
"\n" +
356 " ID: " + generator.uri.spec);
367 getGenerator:
function MSS_getGenerator(generatorURI) {
368 return this._localGenerators[generatorURI.spec] ||
380 addGenerator:
function MSS_addGenerator(generatorURI) {
383 function MSS_addGeneratorCallback(resource) {
384 try { t._handleNewGenerator(resource) }
385 finally { resource.destroy() }
389 resource.load(callback);
392 _handleNewGenerator:
function MSS__handleNewGenerator(resource) {
394 throw(resource.uri.spec +
" microsummary generator loaded, but not XML");
398 var rootNode = resource.content.documentElement;
403 rootNode.setAttribute(
"uri",
"urn:source:" + resource.uri.spec);
405 this.installGenerator(resource.content);
417 installGenerator:
function MSS_installGenerator(xmlDefinition) {
418 var rootNode = xmlDefinition.getElementsByTagNameNS(
MICSUM_NS,
"generator")[0];
420 var generatorID = rootNode.getAttribute(
"uri");
423 var generator = this._localGenerators[generatorID];
427 topic =
"microsummary-generator-updated";
430 topic =
"microsummary-generator-installed";
431 var generatorName = rootNode.getAttribute(
"name");
433 var
file = this._dirs.get(
"UsrMicsumGens",
Ci.nsIFile);
434 file.append(fileName);
435 file.createUnique(
Ci.nsIFile.NORMAL_FILE_TYPE,
PERMS_FILE);
437 this._localGenerators[generatorID] = generator;
442 generator.initFromXML(xmlDefinition);
443 generator.saveXMLToFile(xmlDefinition);
445 LOG(
"installed generator " + generatorID);
447 this.
_obs.notifyObservers(generator, topic,
null);
474 getMicrosummaries:
function MSS_getMicrosummaries(pageURI, bookmarkID) {
477 if (!
getPref(
"browser.microsummary.enabled",
true))
478 return microsummaries;
481 for (var genURISpec in this._localGenerators) {
482 var generator = this._localGenerators[genURISpec];
484 if (generator.appliesToURI(pageURI)) {
485 var microsummary =
new Microsummary(pageURI, generator);
489 if (bookmarkID != -1 && this.isMicrosummary(bookmarkID, microsummary))
490 microsummary._content = this._bms.getItemTitle(bookmarkID);
492 microsummaries.AppendElement(microsummary);
498 if (bookmarkID != -1 && this.hasMicrosummary(bookmarkID)) {
499 var currentMicrosummary = this.getMicrosummary(bookmarkID);
500 if (!microsummaries.hasItemForMicrosummary(currentMicrosummary))
501 microsummaries.AppendElement(currentMicrosummary);
508 try { microsummaries.extractFromPage(resource) }
509 finally { resource.destroy() }
514 var
callback =
function MSS_extractFromPageCallback(resource) {
515 try { microsummaries.extractFromPage(resource) }
516 finally { resource.destroy() }
521 resource.load(callback);
529 LOG(
"error downloading page to extract its microsummaries: " + e);
533 return microsummaries;
547 _changeField:
function MSS__changeField(fieldName, oldValue, newValue) {
548 var bookmarks = this._bookmarks;
550 for ( var
i = 0;
i < bookmarks.length;
i++ ) {
551 var bookmarkID = bookmarks[
i];
553 if (this._ans.itemHasAnnotation(bookmarkID, fieldName) &&
554 this._ans.getItemAnnotation(bookmarkID, fieldName) == oldValue)
555 this._setAnnotation(bookmarkID, fieldName, newValue);
572 return this._bookmarks;
575 _setAnnotation:
function MSS__setAnnotation(aBookmarkId, aFieldName, aFieldValue) {
576 this._ans.setItemAnnotation(aBookmarkId,
580 this._ans.EXPIRE_NEVER);
593 getBookmarks:
function MSS_getBookmarks() {
607 getMicrosummary:
function MSS_getMicrosummary(bookmarkID) {
608 if (!this.hasMicrosummary(bookmarkID))
611 var pageURI = this._bms.getBookmarkURI(bookmarkID);
612 var generatorURI = this._uri(this._ans.getItemAnnotation(bookmarkID,
614 var generator = this.getGenerator(generatorURI);
631 createMicrosummary:
function MSS_createMicrosummary(pageURI, generatorURI) {
632 var generator = this.getGenerator(generatorURI);
646 setMicrosummary:
function MSS_setMicrosummary(bookmarkID, microsummary) {
649 if (microsummary.content)
650 this._updateMicrosummary(bookmarkID, microsummary);
652 this.refreshMicrosummary(bookmarkID);
662 removeMicrosummary:
function MSS_removeMicrosummary(bookmarkID) {
665 this._bms.setItemTitle(bookmarkID,
this._ans.getItemAnnotation(bookmarkID,
ANNO_STATIC_TITLE));
672 for (let
i = 0;
i < fields.length;
i++) {
673 var field = fields[
i];
674 if (this._ans.itemHasAnnotation(bookmarkID, field))
675 this._ans.removeItemAnnotation(bookmarkID, field);
689 hasMicrosummary:
function MSS_hasMicrosummary(aBookmarkId) {
690 return (this._bookmarks.indexOf(aBookmarkId) != -1);
707 isMicrosummary:
function MSS_isMicrosummary(aBookmarkID, aMicrosummary) {
708 if (!aMicrosummary || !aBookmarkID)
709 throw Cr.NS_ERROR_INVALID_ARG;
711 if (this.hasMicrosummary(aBookmarkID)) {
712 var currentMicrosummarry = this.getMicrosummary(aBookmarkID);
713 if (aMicrosummary.equals(currentMicrosummarry))
737 refreshMicrosummary:
function MSS_refreshMicrosummary(bookmarkID) {
738 if (!this.hasMicrosummary(bookmarkID))
739 throw "bookmark " + bookmarkID +
" does not have a microsummary";
741 var pageURI = this._bms.getBookmarkURI(bookmarkID);
743 throw(
"can't get URL for bookmark with ID " + bookmarkID);
744 var generatorURI = this._uri(this._ans.getItemAnnotation(bookmarkID,
747 var generator = this._localGenerators[generatorURI.spec] ||
750 var microsummary =
new Microsummary(pageURI, generator);
756 _bookmarkID: bookmarkID,
757 onContentLoaded:
function MSS_observer_onContentLoaded(microsummary) {
759 this._svc._updateMicrosummary(this._bookmarkID, microsummary);
762 Cu.reportError(
"refreshMicrosummary() observer: " + ex);
766 this._bookmarkID =
null;
767 microsummary.removeObserver(
this);
771 onError:
function MSS_observer_onError(microsummary) {
772 if (microsummary.needsRemoval)
773 this._svc.removeMicrosummary(this._bookmarkID);
779 microsummary.addObserver(observer);
780 microsummary.update();
786 onItemAnnotationSet:
function(aItemId, aAnnotationName) {
788 this._bookmarks.indexOf(aItemId) == -1)
789 this._bookmarks.push(aItemId);
791 onItemAnnotationRemoved:
function(aItemId, aAnnotationName) {
792 var index = this._bookmarks.indexOf(aItemId);
794 !aAnnotationName.length;
795 if (index > -1 && isMicsumAnno)
796 this._bookmarks.splice(index, 1);
798 onPageAnnotationSet:
function(aUri, aAnnotationName) {},
799 onPageAnnotationRemoved:
function(aUri, aAnnotationName) {},
807 this.bookmarkID = bookmarkID;
808 this.microsummary = microsummary;
816 QueryInterface: XPCOMUtils.generateQI([
Ci.nsILiveTitleNotificationSubject]),
824 this._observers = [];
825 this._pageURI = aPageURI ||
null;
826 this._generator = aGenerator ||
null;
827 this._content =
null;
828 this._pageContent =
null;
829 this._updateInterval =
null;
830 this._needsRemoval =
false;
838 this.__mss =
Cc[
"@mozilla.org/microsummary/service;1"].
847 this.__ios =
Cc[
"@mozilla.org/network/io-service;1"].
858 _uri:
function MSS__uri(spec) {
859 return this._ios.newURI(spec,
null,
null);
868 if (!this._content &&
869 this.generator.loaded &&
870 (
this.pageContent || !
this.generator.needsPageContent)) {
871 this._content = this.generator.generateMicrosummary(this.pageContent);
872 this._updateInterval = this.generator.calculateUpdateInterval(this.pageContent);
880 return this._content;
883 get generator() {
return this._generator },
884 set generator(newValue) {
return this._generator = newValue },
886 get pageURI() {
return this._pageURI },
888 equals:
function(aOther) {
889 if (this._generator &&
890 this._pageURI.equals(aOther.pageURI) &&
891 this._generator.equals(aOther.generator))
898 if (!this._pageContent) {
902 this._pageContent = resource.content;
907 return this._pageContent;
909 set pageContent(newValue) {
return this._pageContent = newValue },
911 get updateInterval() {
return this._updateInterval; },
912 set updateInterval(newValue) {
return this._updateInterval = newValue; },
914 get needsRemoval() {
return this._needsRemoval; },
921 if (this._observers.indexOf(
observer) == -1)
931 if (this._observers.indexOf(
observer) != -1)
932 this._observers.splice(
this._observers.indexOf(
observer), 1);
940 update:
function MS_update() {
941 LOG(
"microsummary.update called for page:\n " + this.pageURI.spec +
942 "\nwith generator:\n " +
this.generator.uri.spec);
948 var errorCallback =
function MS_errorCallback(resource) {
949 if (resource.status == 410) {
950 t._needsRemoval =
true;
951 LOG(
"server indicated " + resource.uri.spec +
" is gone. flagging for removal");
956 for (let
i = 0;
i < t._observers.length;
i++)
957 t._observers[
i].onError(t);
962 if (!this.generator.loaded) {
965 if (this.generator.uri.scheme ==
"urn") {
969 if (/^source:/.
test(this.generator.uri.path)) {
970 this._reinstallMissingGenerator();
974 throw "missing local generator: " + this.generator.uri.spec;
977 LOG(
"generator not yet loaded; downloading it");
978 var generatorCallback =
979 function MS_generatorCallback(resource) {
980 try { t._handleGeneratorLoad(resource) }
981 finally { resource.destroy() }
984 resource.load(generatorCallback, errorCallback);
990 if (this.generator.needsPageContent && !
this.pageContent) {
991 LOG(
"page content not yet loaded; downloading it");
993 function MS_pageCallback(resource) {
994 try { t._handlePageLoad(resource) }
995 finally { resource.destroy() }
998 resource.load(pageCallback, errorCallback);
1002 LOG(
"generator (and page, if needed) both loaded; generating microsummary");
1006 this._content = this.generator.generateMicrosummary(this.pageContent);
1007 this._updateInterval = this.generator.calculateUpdateInterval(this.pageContent);
1008 this.pageContent =
null;
1009 for ( var
i = 0;
i < this._observers.length;
i++ )
1010 this._observers[
i].onContentLoaded(
this);
1012 LOG(
"generated microsummary: " + this.content);
1015 _handleGeneratorLoad:
function MS__handleGeneratorLoad(resource) {
1016 LOG(this.generator.uri.spec +
" microsummary generator downloaded");
1018 this.generator.initFromXML(resource.content);
1019 else if (resource.contentType ==
"text/plain")
1020 this.generator.initFromText(resource.content);
1021 else if (resource.contentType ==
"text/html")
1022 this.generator.initFromText(resource.content.body.textContent);
1024 throw(
"generator is neither XML nor plain text");
1027 if (this.generator.loaded)
1031 _handlePageLoad:
function MS__handlePageLoad(resource) {
1032 if (!resource.isXML && resource.contentType !=
"text/html")
1033 throw(
"page is neither HTML nor XML");
1035 this.pageContent = resource.content;
1044 _reinstallMissingGenerator:
function MS__reinstallMissingGenerator() {
1045 LOG(
"attempting to reinstall missing generator " + this.generator.uri.spec);
1050 function MS_missingGeneratorLoadCallback(resource) {
1051 try { t._handleMissingGeneratorLoad(resource) }
1052 finally { resource.destroy() }
1056 function MS_missingGeneratorErrorCallback(resource) {
1057 try { t._handleMissingGeneratorError(resource) }
1058 finally { resource.destroy() }
1063 var sourceURL = this.generator.uri.path.replace(/^source:/,
"");
1064 var sourceURI = this._uri(sourceURL);
1067 resource.load(loadCallback, errorCallback);
1071 this._handleMissingGeneratorError();
1086 _handleMissingGeneratorLoad:
function MS__handleMissingGeneratorLoad(resource) {
1089 if (!resource.isXML)
1090 throw(
"downloaded, but not XML " + this.generator.uri.spec);
1093 var generatorID = this.generator.uri.spec;
1094 resource.content.documentElement.setAttribute(
"uri", generatorID);
1098 this.generator = this._mss.installGenerator(resource.content);
1103 if (!this.generator.loaded)
1104 throw(
"supposedly installed, but not in cache " + this.generator.uri.spec);
1108 this._handleMissingGeneratorError(resource);
1112 LOG(
"reinstall succeeded; resuming update " + this.generator.uri.spec);
1126 _handleMissingGeneratorError:
function MS__handleMissingGeneratorError(resource) {
1127 LOG(
"reinstall failed; removing microsummaries " + this.generator.uri.spec);
1128 var bookmarks = this._mss.getBookmarks();
1129 while (bookmarks.hasMoreElements()) {
1130 var bookmarkID = bookmarks.getNext();
1131 var microsummary = this._mss.getMicrosummary(bookmarkID);
1132 if (microsummary.generator.uri.equals(
this.generator.uri)) {
1133 LOG(
"removing microsummary for " + microsummary.pageURI.spec);
1134 this._mss.removeMicrosummary(bookmarkID);
1146 this._uri = aURI ||
null;
1147 this._localURI = aLocalURI ||
null;
1149 this._loaded =
false;
1151 this._template =
null;
1152 this._content =
null;
1161 this.__ios =
Cc[
"@mozilla.org/network/io-service;1"].
1175 get uri() {
return this._uri || this.localURI },
1179 get localURI() {
return this._localURI },
1180 get name() {
return this._name },
1181 get loaded() {
return this._loaded },
1183 equals:
function(aOther) {
1185 return aOther.uri.equals(this.
uri);
1201 appliesToURI:
function(
uri) {
1202 var applies =
false;
1204 for ( var
i = 0 ;
i < this._rules.length ;
i++ ) {
1205 var rule = this._rules[
i];
1207 switch (rule.type) {
1209 if (rule.regexp.test(
uri.spec))
1213 if (rule.regexp.test(
uri.spec))
1222 get needsPageContent() {
1228 throw(
"needsPageContent called on uninitialized microsummary generator");
1239 initFromText:
function(text) {
1240 this._content = text;
1241 this._loaded =
true;
1251 initFromXML:
function(xmlDocument) {
1261 var generatorNode = xmlDocument.getElementsByTagNameNS(
MICSUM_NS,
"generator")[0];
1263 throw Cr.NS_ERROR_FAILURE;
1265 this._name = generatorNode.getAttribute(
"name");
1269 if (this.localURI && generatorNode.hasAttribute(
"uri"))
1270 this._uri = this._ios.newURI(generatorNode.getAttribute(
"uri"),
null,
null);
1272 function getFirstChildByTagName(tagName, parentNode,
namespace) {
1273 var nodeList = parentNode.getElementsByTagNameNS(
namespace, tagName);
1274 for (var
i = 0;
i < nodeList.length;
i++) {
1276 if (nodeList[
i].parentNode == parentNode)
1285 this._rules.splice(0);
1286 var
pages = getFirstChildByTagName(
"pages", generatorNode,
MICSUM_NS);
1289 for ( var
i = 0;
i < pages.childNodes.length ;
i++ ) {
1290 var
node = pages.childNodes[
i];
1291 if (node.nodeType != node.ELEMENT_NODE ||
1293 (node.nodeName !=
"include" && node.nodeName !=
"exclude"))
1295 var urlRegexp = node.textContent.replace(/^\s+|\s+$/g,
"");
1296 this._rules.push({ type: node.nodeName, regexp:
new RegExp(urlRegexp) });
1302 var update = getFirstChildByTagName(
"update", generatorNode,
MICSUM_NS);
1304 function _parseInterval(
string) {
1307 return Math.round(Math.max(parseFloat(
string) || 0, 1) * 60 * 1000);
1310 this._unconditionalUpdateInterval =
1311 update.hasAttribute(
"interval") ?
1312 _parseInterval(update.getAttribute(
"interval")) :
null;
1315 this._updateIntervals =
new Array();
1316 for (
i = 0;
i < update.childNodes.length;
i++) {
1317 node = update.childNodes[
i];
1318 if (node.nodeType != node.ELEMENT_NODE || node.namespaceURI !=
MICSUM_NS ||
1319 node.nodeName !=
"condition")
1321 if (!node.getAttribute(
"expression") || !node.getAttribute(
"interval")) {
1322 LOG(
"ignoring incomplete conditional update interval for " + this.
uri.spec);
1325 this._updateIntervals.push({
1326 expression: node.getAttribute(
"expression"),
1327 interval: _parseInterval(node.getAttribute(
"interval"))
1332 var templateNode = getFirstChildByTagName(
"template", generatorNode,
MICSUM_NS);
1334 this._template = getFirstChildByTagName(
"transform", templateNode,
XSLT_NS) ||
1335 getFirstChildByTagName(
"stylesheet", templateNode,
XSLT_NS);
1339 this._loaded =
true;
1342 generateMicrosummary:
function MSD_generateMicrosummary(pageContent) {
1347 content = this._content;
1348 else if (this._template)
1349 content = this._processTemplate(pageContent);
1351 throw(
"generateMicrosummary called on uninitialized microsummary generator");
1354 content = content.replace(/^\s+|\s+$/g,
"");
1361 calculateUpdateInterval:
function MSD_calculateUpdateInterval(
doc) {
1362 if (this._content || !this._updateIntervals || !
doc)
1365 for (var
i = 0;
i < this._updateIntervals.length;
i++) {
1367 if (
doc.evaluate(
this._updateIntervals[
i].expression,
doc,
null,
1368 Ci.nsIDOMXPathResult.BOOLEAN_TYPE,
null).booleanValue)
1369 return this._updateIntervals[
i].interval;
1374 this._updateIntervals.splice(
i--, 1);
1378 return this._unconditionalUpdateInterval;
1381 _processTemplate:
function MSD__processTemplate(
doc) {
1382 LOG(
"processing template " + this._template +
" against document " +
doc);
1385 var processor =
Cc[
"@mozilla.org/document-transformer;1?type=xslt"].
1390 processor.flags |=
Ci.nsIXSLTProcessorPrivate.DISABLE_ALL_LOADS;
1392 processor.importStylesheet(this._template);
1393 var fragment = processor.transformToFragment(
doc,
doc);
1395 LOG(
"template processing result: " + fragment.textContent);
1399 return fragment.textContent;
1402 saveXMLToFile:
function MSD_saveXMLToFile(xmlDefinition) {
1403 var
file = this.localURI.QueryInterface(
Ci.nsIFileURL).file.clone();
1405 LOG(
"saving definition to " + file.path);
1408 var outputStream =
Cc[
"@mozilla.org/network/safe-file-output-stream;1"].
1410 var localFile = file.QueryInterface(
Ci.nsILocalFile);
1413 var serializer =
Cc[
"@mozilla.org/xmlextras/xmlserializer;1"].
1415 serializer.serializeToStream(xmlDefinition, outputStream,
null);
1416 if (outputStream instanceof
Ci.nsISafeOutputStream) {
1417 try { outputStream.finish() }
1418 catch (e) { outputStream.close() }
1421 outputStream.close();
1424 update:
function MSD_update() {
1427 var genURI = this.
uri;
1428 if (genURI && /^urn:source:/
i.test(genURI.spec)) {
1429 let genURL = genURI.spec.replace(/^urn:source:/,
"");
1430 genURI = this._ios.newURI(genURL,
null,
null);
1434 if (!genURI || !/^https?/.
test(genURI.scheme)) {
1435 LOG(
"generator did not have valid URI; skipping update: " + genURI.spec);
1443 var loadCallback =
function(resource) {
1444 if (resource.status != 304)
1445 t._performUpdate(genURI);
1447 LOG(
"generator is already up to date: " + genURI.spec);
1450 var errorCallback =
function(resource) {
1454 var file = this.localURI.QueryInterface(
Ci.nsIFileURL).file.clone();
1455 var lastmod =
new Date(file.lastModifiedTime);
1456 LOG(
"updating generator: " + genURI.spec);
1458 resource.lastMod = lastmod.toUTCString();
1459 resource.method =
"HEAD";
1460 resource.load(loadCallback, errorCallback);
1463 _performUpdate:
function MSD__performUpdate(
uri) {
1465 var loadCallback =
function(resource) {
1466 try { t._handleUpdateLoad(resource) }
1467 finally { resource.destroy() }
1469 var errorCallback =
function(resource) {
1474 resource.load(loadCallback, errorCallback);
1477 _handleUpdateLoad:
function MSD__handleUpdateLoad(resource) {
1478 if (!resource.isXML)
1479 throw(
"update failed, downloaded resource is not XML: " + this.
uri.spec);
1483 var generatorID = this.
uri.spec;
1484 resource.content.documentElement.setAttribute(
"uri", generatorID);
1487 this.initFromXML(resource.content);
1488 this.saveXMLToFile(resource.content);
1491 var obs =
Cc[
"@mozilla.org/observer-service;1"].
1493 obs.notifyObservers(
this,
"microsummary-generator-updated",
null);
1507 this._observers = [];
1508 this._elements = [];
1516 this.__ios =
Cc[
"@mozilla.org/network/io-service;1"].
1522 Ci.nsIMicrosummaryObserver]),
1526 onContentLoaded:
function MSSet_onContentLoaded(microsummary) {
1527 for ( var
i = 0;
i < this._observers.length;
i++ )
1528 this._observers[
i].onContentLoaded(microsummary);
1531 onError:
function MSSet_onError(microsummary) {
1532 for ( var
i = 0;
i < this._observers.length;
i++ )
1533 this._observers[
i].onError(microsummary);
1539 if (this._observers.length == 0) {
1540 for ( var
i = 0 ;
i < this._elements.length ;
i++ )
1546 if (this._observers.indexOf(
observer) == -1)
1556 if (this._observers.indexOf(
observer) != -1)
1557 this._observers.splice(
this._observers.indexOf(
observer), 1);
1559 if (this._observers.length == 0) {
1560 for ( var
i = 0 ;
i < this._elements.length ;
i++ )
1565 extractFromPage:
function MSSet_extractFromPage(resource) {
1566 if (!resource.isXML && resource.contentType !=
"text/html")
1567 throw(
"page is neither HTML nor XML");
1572 var links = resource.content.getElementsByTagName(
"link");
1573 for ( var
i = 0;
i < links.length;
i++ ) {
1574 var link = links[
i];
1576 if(!link.hasAttribute(
"rel"))
1579 var relAttr = link.getAttribute(
"rel");
1583 var linkTypes = relAttr.split(/\s+/);
1584 if (!linkTypes.some(
function(v) {
return v.toLowerCase() ==
"microsummary"; }))
1589 var linkTitle = link.getAttribute(
"title");
1594 var generatorURI = this._ios.newURI(link.href,
1595 resource.content.characterSet,
1598 if (!/^https?$/
i.test(generatorURI.scheme)) {
1599 LOG(
"can't load generator " + generatorURI.spec +
" from page " +
1605 var microsummary =
new Microsummary(resource.uri, generator);
1606 if (!this.hasItemForMicrosummary(microsummary))
1607 this.AppendElement(microsummary);
1615 hasItemForMicrosummary:
function MSSet_hasItemForMicrosummary(aMicrosummary) {
1616 for (var
i = 0;
i < this._elements.length;
i++) {
1617 if (this._elements[
i].equals(aMicrosummary))
1624 AppendElement:
function MSSet_AppendElement(
element) {
1629 if (this._elements.indexOf(
element) == -1) {
1635 for ( var
i = 0;
i < this._observers.length;
i++ )
1636 this._observers[
i].onElementAppended(
element);
1639 Enumerate:
function MSSet_Enumerate() {
1654 for (var
i = 0;
i < aItems.length; ++
i) {
1656 aItems.splice(i--, 1);
1658 this._contents = aItems;
1660 this._contents = [];
1670 return this._index < this._contents.length;
1673 getNext:
function() {
1674 return this._contents[this._index++];
1691 if (!(
"_enabled" in f))
1692 f._enabled =
getPref(
"browser.microsummary.log",
false);
1694 dump(
"*** Microsummaries: " + aText +
"\n");
1695 var consoleService =
Cc[
"@mozilla.org/consoleservice;1"].
1697 consoleService.logStringMessage(aText);
1720 if (!(
uri.schemeIs(
"http") ||
uri.schemeIs(
"https") ||
uri.schemeIs(
"file")))
1724 this._content =
null;
1725 this._contentType =
null;
1726 this._isXML =
false;
1727 this.__authFailed =
false;
1728 this._status =
null;
1729 this._method =
"GET";
1730 this._lastMod =
null;
1733 this._loadCallback =
null;
1735 this._errorCallback =
null;
1737 this._iframe =
null;
1745 this.__ios =
Cc[
"@mozilla.org/network/io-service;1"].
1755 return this._content;
1759 return this._contentType;
1766 get status() {
return this._status },
1767 set status(aStatus) { this._status = aStatus },
1769 get method() {
return this._method },
1770 set method(aMethod) { this._method = aMethod },
1772 get lastMod() {
return this._lastMod },
1773 set lastMod(aMod) { this._lastMod = aMod },
1781 Ci.nsIBadCertListener2,
1782 Ci.nsISSLErrorListener,
1784 Ci.nsIProgressEventSink,
1785 Ci.nsIInterfaceRequestor,
1791 if (!this.
interfaces.some(
function(v) {
return iid.equals(v) } ))
1792 throw Cr.NS_ERROR_NO_INTERFACE;
1798 case Ci.nsIAuthPrompt:
1799 return this.authPrompt;
1809 getInterface:
function MSR_getInterface(iid) {
1815 notifyCertProblem:
function MSR_certProblem(socketInfo, status, targetSite) {
1821 notifySSLError:
function MSR_SSLError(socketInfo, error, targetSite) {
1831 get _authFailed() {
return this.__authFailed; },
1832 set _authFailed(newValue) {
return this.__authFailed = newValue },
1836 getAuthPrompt:
function(aPromptReason, aIID) {
1837 this._authFailed =
true;
1838 throw Cr.NS_ERROR_NOT_AVAILABLE;
1848 var resource =
this;
1851 prompt:
function(dialogTitle, text, passwordRealm, savePassword, defaultText, result) {
1852 resource._authFailed =
true;
1855 promptUsernameAndPassword:
function(dialogTitle, text, passwordRealm, savePassword, user, pwd) {
1856 resource._authFailed =
true;
1859 promptPassword:
function(dialogTitle, text, passwordRealm, savePassword, pwd) {
1860 resource._authFailed =
true;
1869 var resource =
this;
1872 alert:
function(dialogTitle, text) {
1873 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
1875 alertCheck:
function(dialogTitle, text, checkMessage, checkValue) {
1876 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
1878 confirm:
function(dialogTitle, text) {
1879 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
1881 confirmCheck:
function(dialogTitle, text, checkMessage, checkValue) {
1882 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
1884 confirmEx:
function(dialogTitle, text, buttonFlags, button0Title, button1Title, button2Title, checkMsg, checkValue) {
1885 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
1887 prompt:
function(dialogTitle, text,
value, checkMsg, checkValue) {
1888 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
1890 promptPassword:
function(dialogTitle, text, password, checkMsg, checkValue) {
1891 resource._authFailed =
true;
1894 promptUsernameAndPassword:
function(dialogTitle, text, username, password, checkMsg, checkValue) {
1895 resource._authFailed =
true;
1898 select:
function(dialogTitle, text,
count, selectList, outSelection) {
1899 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
1912 onProgress:
function(aRequest, aContext, aProgress, aProgressMax) {},
1913 onStatus:
function(aRequest, aContext, aStatus, aStatusArg) {},
1922 initFromDocument:
function MSR_initFromDocument(document) {
1923 this._content = document;
1924 this._contentType = document.contentType;
1930 this._isXML = (this.contentType ==
"text/xml" ||
1931 this.contentType ==
"application/xml" ||
1932 /^.+\/.+\+xml$/.test(this.contentType));
1940 destroy:
function MSR_destroy() {
1942 this._content =
null;
1943 this._loadCallback =
null;
1944 this._errorCallback =
null;
1945 this._loadTimer =
null;
1946 this._authFailed =
false;
1948 if (this._iframe && this._iframe.parentNode)
1949 this._iframe.parentNode.removeChild(this._iframe);
1950 this._iframe =
null;
1963 load:
function MSR_load(loadCallback, errorCallback) {
1964 LOG(this.
uri.spec +
" loading");
1966 this._loadCallback = loadCallback;
1967 this._errorCallback = errorCallback;
1969 var request =
Cc[
"@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
1973 handleEvent:
function MSR_loadHandler_handleEvent(
event) {
1974 if (this._self._loadTimer)
1975 this._self._loadTimer.cancel();
1977 this._self.status =
event.target.status;
1979 if (this._self._authFailed ||
this._self.status >= 400) {
1986 LOG(this._self.uri.spec +
" load failed; HTTP status: " +
this._self.status);
1987 try { this._self._handleError(
event) }
1988 finally { this._self =
null }
1990 else if (
event.target.channel.contentType ==
"multipart/x-mixed-replace") {
1993 LOG(this._self.uri.spec +
" load failed; contains multipart content");
1994 try { this._self._handleError(
event) }
1995 finally { this._self =
null }
1998 LOG(this._self.uri.spec +
" load succeeded; invoking callback");
1999 try { this._self._handleLoad(
event) }
2000 finally { this._self =
null }
2005 var errorHandler = {
2007 handleEvent:
function MSR_errorHandler_handleEvent(
event) {
2008 if (this._self._loadTimer)
2009 this._self._loadTimer.cancel();
2011 LOG(this._self.uri.spec +
" load failed");
2012 try { this._self._handleError(
event) }
2013 finally { this._self =
null }
2020 var timeout =
getPref(
"browser.microsummary.requestTimeout", 300) * 1000;
2021 var timerObserver = {
2023 observe:
function MSR_timerObserver_observe() {
2024 LOG(
"timeout loading microsummary resource " + this._self.uri.spec +
", aborting request");
2026 try { this._self.destroy() }
2027 finally { this._self =
null }
2030 this._loadTimer =
Cc[
"@mozilla.org/timer;1"].createInstance(
Ci.nsITimer);
2031 this._loadTimer.init(timerObserver, timeout,
Ci.nsITimer.TYPE_ONE_SHOT);
2033 request = request.QueryInterface(
Ci.nsIDOMEventTarget);
2034 request.addEventListener(
"load", loadHandler,
false);
2035 request.addEventListener(
"error", errorHandler,
false);
2037 request = request.QueryInterface(
Ci.nsIXMLHttpRequest);
2038 request.open(this.method, this.
uri.spec,
true);
2039 request.setRequestHeader(
"X-Moz",
"microsummary");
2041 request.setRequestHeader(
"If-Modified-Since", this.lastMod);
2046 request.channel.notificationCallbacks =
this;
2053 var resolver =
Cc[
"@mozilla.org/embeddor.implemented/bookmark-charset-resolver;1"].
2056 var
charset = resolver.requestCharset(
null, request.channel, {}, {});
2058 request.channel.contentCharset =
charset;
2066 _handleLoad:
function MSR__handleLoad(
event) {
2067 var request =
event.target;
2069 if (request.responseXML) {
2072 if (request.responseXML.documentElement.nodeName ==
"parsererror") {
2073 this._handleError(
event);
2076 this._content = request.responseXML;
2077 this._contentType = request.channel.contentType;
2078 this._loadCallback(
this);
2081 else if (request.channel.contentType ==
"text/html") {
2082 this._parse(request.responseText);
2088 this._content = request.responseText;
2089 this._contentType = request.channel.contentType;
2090 this._loadCallback(
this);
2094 _handleError:
function MSR__handleError(
event) {
2096 try {
if (this._errorCallback) this._errorCallback(
this) }
2097 finally { this.destroy() }
2109 _parse:
function MSR__parse(htmlText) {
2111 var windowMediator =
Cc[
'@mozilla.org/appshell/window-mediator;1'].
2113 var
window = windowMediator.getMostRecentWindow(
"navigator:browser");
2120 this._handleError(
event);
2123 var document = window.document;
2124 var rootElement = document.documentElement;
2127 this._iframe = document.createElement(
'iframe');
2128 this._iframe.setAttribute(
"collapsed",
true);
2129 this._iframe.setAttribute(
"type",
"content");
2132 rootElement.appendChild(this._iframe);
2137 var webNav = this._iframe.docShell.QueryInterface(
Ci.nsIWebNavigation);
2138 webNav.stop(
Ci.nsIWebNavigation.STOP_NETWORK);
2143 this._iframe.docShell.allowJavascript =
false;
2144 this._iframe.docShell.allowAuth =
false;
2145 this._iframe.docShell.allowPlugins =
false;
2146 this._iframe.docShell.allowMetaRedirects =
false;
2147 this._iframe.docShell.allowSubframes =
false;
2148 this._iframe.docShell.allowImages =
false;
2149 this._iframe.docShell.allowDNSPrefetch =
false;
2151 var parseHandler = {
2153 handleEvent:
function MSR_parseHandler_handleEvent(
event) {
2154 event.target.removeEventListener(
"DOMContentLoaded",
this,
false);
2155 try { this._self._handleParse(
event) }
2156 finally { this._self =
null }
2161 var
converter =
Cc[
"@mozilla.org/intl/scriptableunicodeconverter"].
2163 converter.charset =
"UTF-8";
2164 var stream = converter.convertToInputStream(htmlText);
2167 var channel =
Cc[
"@mozilla.org/network/input-stream-channel;1"].
2169 channel.setURI(this._uri);
2170 channel.contentStream = stream;
2173 var request = channel.QueryInterface(
Ci.nsIRequest);
2174 request.loadFlags |=
Ci.nsIRequest.LOAD_BACKGROUND;
2179 var baseChannel = channel.QueryInterface(
Ci.nsIChannel);
2180 baseChannel.contentType =
"text/html";
2185 baseChannel.contentCharset =
"UTF-8";
2190 this._iframe.addEventListener(
"DOMContentLoaded", parseHandler,
true);
2191 var uriLoader =
Cc[
"@mozilla.org/uriloader;1"].getService(
Ci.nsIURILoader);
2192 uriLoader.openURI(channel,
true, this._iframe.docShell);
2202 _handleParse:
function MSR__handleParse(
event) {
2205 this._content = this._iframe.contentDocument;
2206 this._contentType = this._iframe.contentDocument.contentType;
2207 this._loadCallback(
this);
2224 var mediator =
Cc[
"@mozilla.org/appshell/window-mediator;1"].
2229 var
windows = mediator.getEnumerator(
"navigator:browser");
2231 while (windows.hasMoreElements()) {
2232 var
win = windows.getNext();
2233 var tabBrowser = win.document.getElementById(
"content");
2234 for ( var
i = 0;
i < tabBrowser.browsers.length;
i++ ) {
2235 var
browser = tabBrowser.browsers[
i];
2236 if (
uri.equals(browser.currentURI)) {
2238 resource.initFromDocument(browser.contentDocument);
2256 var prefBranch =
Cc[
"@mozilla.org/preferences-service;1"].
2258 var type = prefBranch.getPrefType(prefName);
2260 case prefBranch.PREF_BOOL:
2261 return prefBranch.getBoolPref(prefName);
2262 case prefBranch.PREF_INT:
2263 return prefBranch.getIntPref(prefName);
2268 return defaultValue;
2282 const chars =
"-abcdefghijklmnopqrstuvwxyz0123456789";
2283 const maxLength = 60;
2286 name = name.replace(/ /g,
"-");
2290 var filteredName =
"";
2291 for ( var
i = 0 ;
i < name.length ;
i++ )
2292 if (
chars.indexOf(name[
i]) != -1)
2293 filteredName += name[i];
2294 name = filteredName;
2298 for (var i = 0; i < 8; ++
i)
2299 name +=
chars.charAt(Math.round(Math.random() * (
chars.length - 1)));
2302 if (name.length > maxLength)
2303 name = name.substring(0, maxLength);
function MicrosummarySet()
prefs removeObserver(PREF_SELECTED_ACTION, this)
function sanitizeName(aName)
Wraps a js array in an nsISimpleEnumerator.
function doc() browser.contentDocument
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
function getPref(prefName, defaultValue)
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
function MicrosummaryResource(uri)
const ANNO_MICSUM_EXPIRATION
getService(Ci.sbIFaceplateManager)
sbDeviceFirmwareAutoCheckForUpdate prototype _timer
const NS_ERROR_DOM_BAD_URI
TimerLoop prototype notify
historySvc addObserver(this, false)
function Microsummary(aPageURI, aGenerator)
function MicrosummaryService()
return!aWindow arguments!aWindow arguments[0]
SimpleArrayEnumerator prototype hasMoreElements
function MicrosummaryGenerator(aURI, aLocalURI, aName)
sbDeviceFirmwareAutoCheckForUpdate prototype classID
const ANNO_MICSUM_GEN_URI
sbWindowsAutoPlayServiceCfg _xpcom_categories
const NS_ERROR_MODULE_DOM
sbDeviceFirmwareAutoCheckForUpdate prototype interfaces
__defineGetter__("Application", function(){delete this.Application;return this.Application=Cc["@mozilla.org/fuel/application;1"].getService(Ci.fuelIApplication);})
function LiveTitleNotificationSubject(bookmarkID, microsummary)
_getSelectedPageStyle s i
function getLoadedMicrosummaryResource(uri)
sbDeviceFirmwareAutoCheckForUpdate prototype observe
function ArrayEnumerator(aItems)
function NSGetModule(compMgr, fileSpec)