Querying if a URI was visited? Topic is solved

Add-ons for Pale Moon and other applications
General discussion, compatibility, contributed extensions, themes, plugins, and more.

Moderators: Lootyhoof, FranklinDM

User avatar
GreySkyDawn
Moongazer
Moongazer
Posts: 7
Joined: 2025-04-10, 08:22

Querying if a URI was visited?

Unread post by GreySkyDawn » 2025-05-14, 13:03

Hi, I'm developing an extension for my personal use, and I need a simple way to test if a certain URI was visited or not. I know that the answer lies in the Places API, maybe using nsIGlobalHistory2 , but try as I might I can't get an object of that interface. Can anyone help me with a short code snippet that I can test in the Browser Console and performs such a test?

What I tried so far:

Code: Select all

historyService = Components.classes["@mozilla.org/browser/nav-history-service;1"]
                               .getService(Components.interfaces.nsINavHistoryService);
gh = historyService.QueryInterface(Ci.nsIGlobalHistory2)
leads to error 'NS_NOINTERFACE'

Fiddling with similar snippets also leads to errors. I think it's time to ask someone more knowelgable :)

vannilla
Moon Magic practitioner
Moon Magic practitioner
Posts: 2407
Joined: 2018-05-05, 13:29

Re: Querying if a URI was visited?

Unread post by vannilla » 2025-05-14, 13:46

Have you tried nsINavHistoryService following this page: http://udn.realityripple.com/docs/Mozil ... ry_service ?
I have never used the Places API, just throwing ideas.

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-14, 14:10

Try this:

Code: Select all

var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var testURI = ios.newURI("http://google.com/", null, null);
var gh = Cc["@mozilla.org/browser/global-history;2"].getService(Ci.nsIGlobalHistory2);
gh.isVisited(testURI);
https://repo.palemoon.org/MoonchildProd ... History.js

User avatar
GreySkyDawn
Moongazer
Moongazer
Posts: 7
Joined: 2025-04-10, 08:22

Re: Querying if a URI was visited?

Unread post by GreySkyDawn » 2025-05-14, 17:14

vannilla wrote:
2025-05-14, 13:46
Have you tried nsINavHistoryService following this page: http://udn.realityripple.com/docs/Mozil ... ry_service ?
I have never used the Places API, just throwing ideas.
I've read it but try as I might I can't create an instance of nsIGlobalHistory2

I've tried the code snippet given by Kris_88 above, but

Code: Select all

Cc["@mozilla.org/browser/global-history;2"]
always returns undefined (other components, including e.g "@mozilla.org/browser/nav-history-service;1", are fine).

edit: also tested on another computer & clean profile. Tested with PM 33.7.1

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-14, 17:52

GreySkyDawn wrote:
2025-05-14, 17:14

Code: Select all

Cc["@mozilla.org/browser/global-history;2"]
always returns undefined (other components, including e.g "@mozilla.org/browser/nav-history-service;1", are fine).
edit: also tested on another computer & clean profile. Tested with PM 33.7.1
I'm afraid there is only asynchronous method then.
I checked, it works:

Code: Select all

  Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
  var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  var testURI = ios.newURI("https://google.com/", null, null);
  PlacesUtils.asyncHistory.isURIVisited(testURI, (aURI, aIsVisited) => {
     // aIsVisited - link status
  });

User avatar
Moonchild
Pale Moon guru
Pale Moon guru
Posts: 37676
Joined: 2011-08-28, 17:27
Location: Motala, SE

Re: Querying if a URI was visited?

Unread post by Moonchild » 2025-05-14, 18:00

Yes it seems the synchronous interfaces were retired a long while ago.
"A dead end street is a place to turn around and go into a new direction" - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

User avatar
tellu-white
Fanatic
Fanatic
Posts: 191
Joined: 2022-03-08, 22:02

Re: Querying if a URI was visited?

Unread post by tellu-white » 2025-05-14, 18:31

With the following code you can also check how many entries with the searched url exist in History:

Code: Select all

function query_History(uri, options) {
	try{
		var query = PlacesUtils.history.getNewQuery();
		query.uri = uri;
		
		var res = PlacesUtils.history.executeQuery(query, options);
		res.root.containerOpen = true;
		
		var results = [];
		
		for (var i = 0; i < res.root.childCount; i++){
			results.push(res.root.getChild(i));
		}
		
		res.root.containerOpen = false;
		
		return results;
		
	} catch(err){
		alert(err.message);
	}
}

function set_query_options(uri) {
	try{
		var options = PlacesUtils.history.getNewQueryOptions();
		options.queryType = Components.interfaces.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
		options.resultType = Components.interfaces.nsINavHistoryQueryOptions.RESULTS_AS_VISIT;
		
		return query_History(uri, options);
		
	} catch(err){
		alert(err.message);
	}
}

// var page_URL = "https://en.wikipedia.org/wiki/Déjà_vu";

var page_URL = "https://en.wikipedia.org/wiki/D%C3%A9j%C3%A0_vu";

var page_URL_decoded = decodeURIComponent(page_URL);

var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(page_URL_decoded, null, null);

var arr_url_in_history = set_query_options(uri);

var arr_url_in_history_length = arr_url_in_history.length;

alert("Number of Entries = " + arr_url_in_history_length);

if(arr_url_in_history_length > 0){
	alert('URL = "' + decodeURIComponent(arr_url_in_history[0].uri) +  '" is in History');
	
} else{
	alert('URL = "' + page_URL_decoded + '" is NOT in History');
}

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-14, 18:34

And yes, it can be done without PlacesUtils.jsm

Code: Select all

  var ah = Components.classes["@mozilla.org/browser/history;1"].getService(Components.interfaces.mozIAsyncHistory);
  var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  var testURI = ios.newURI("https://forum.palemoon.org/", null, null);
  ah.isURIVisited(testURI, (aURI, aIsVisited) => {
     // aIsVisited
  });

User avatar
Moonchild
Pale Moon guru
Pale Moon guru
Posts: 37676
Joined: 2011-08-28, 17:27
Location: Motala, SE

Re: Querying if a URI was visited?

Unread post by Moonchild » 2025-05-14, 18:36

It always serves to check the various .jsms available, instead of talking XPCOM directly. It tends to be easier ;)
Services.jsm is also a massive recommendation for a lot of the common APIs.
"A dead end street is a place to turn around and go into a new direction" - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-14, 19:12

Moonchild wrote:
2025-05-14, 18:36
It always serves to check the various .jsms available, instead of talking XPCOM directly.
I usually follow the context. If services.jsm is already imported, I try to use it. If it is not imported or I am not sure and I need one or two functions, I prefer to call getService myself.

User avatar
Moonchild
Pale Moon guru
Pale Moon guru
Posts: 37676
Joined: 2011-08-28, 17:27
Location: Motala, SE

Re: Querying if a URI was visited?

Unread post by Moonchild » 2025-05-14, 20:33

It just depends on what you're more comfortable with and what makes the most sense for a given situation.
"A dead end street is a place to turn around and go into a new direction" - Anonymous
"Seek wisdom, not knowledge. Knowledge is of the past; wisdom is of the future." -- Native American proverb
"Linux makes everything difficult." -- Lyceus Anubite

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-14, 22:25

Looks like I found a synchronous, deprecated but working function that searches through history...

Code: Select all

var hs = Components.classes["@mozilla.org/browser/nav-history-service;1"].getService(Components.interfaces.nsINavHistoryService);
var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var testURI = ios.newURI("https://forum.palemoon.org/", null, null);
var isVisited = typeof hs.getPageTitle(testURI) == 'string';

User avatar
GreySkyDawn
Moongazer
Moongazer
Posts: 7
Joined: 2025-04-10, 08:22

Re: Querying if a URI was visited?

Unread post by GreySkyDawn » 2025-05-15, 01:22

Kris_88 wrote:
2025-05-14, 17:52
I'm afraid there is only asynchronous method then.
I checked, it works:
(code...)
Works perfectly; thanks a lot!

User avatar
_yup_
Moongazer
Moongazer
Posts: 13
Joined: 2025-04-26, 11:45

Re: Querying if a URI was visited?

Unread post by _yup_ » 2025-05-15, 15:02

Kris_88 wrote:
2025-05-14, 22:25
Looks like I found a synchronous, deprecated but working function that searches through history...
"Whoever dives deepest"

Code: Select all

var url = 'https://forum.palemoon.org/';

var sql1 = 'SELECT id FROM moz_places WHERE url = "';
var sql2 = '" AND last_visit_date NOTNULL';
var sql_stmt = Components.classes['@mozilla.org/browser/nav-history-service;1']
                         .getService(Components.interfaces.nsPIPlacesDatabase)
                         .DBConnection.createStatement(sql1 + url + sql2);
var isVisited = sql_stmt.executeStep();
sql_stmt.finalize();
But there is an ambiguity in the initial question: should downloads be counted as visited URIs or not?
If the latter, then "visit_count <> 0" must be used instead of "last_visit_date NOTNULL" in my code. But what about other methods?

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-15, 15:30

_yup_ wrote:
2025-05-15, 15:02
"Whoever dives deepest"
:D
_yup_ wrote:
2025-05-15, 15:02

Code: Select all

var url = 'https://forum.palemoon.org/';
...
sql_stmt.finalize();
This is a bad solution.

1) It is tied to the table structure.
2) You have a vulnerability - it is possible to inject SQL statements via a specially formed URL.
3) It is long.

User avatar
_yup_
Moongazer
Moongazer
Posts: 13
Joined: 2025-04-26, 11:45

Re: Querying if a URI was visited?

Unread post by _yup_ » 2025-05-15, 16:52

Kris_88 wrote:
2025-05-15, 15:30
This is a bad solution.
It's rather "just for the completeness of the collection".
But sometimes this is the only way to get the desired result.
1) It is tied to the table structure.
It is compatible with all previous browser versions.
Yes, it can become broken any day. But the same is true for other methods (as nsIGlobalHistory2 from the initial message).
2) You have a vulnerability - it is possible to inject SQL statements via a specially formed URL.
This depends on two things:
1. The source of that URL.
2. The foresight of the person who will use it.

And in any case, every other method use a similar query at the end of call chain. How can one be sure that nsINavHistoryQuery or getPageTitle is not prone to such injection?
3) It is long.
Just for readability. But it can be simplified to 3 operators - as well as its competitors :geek:
And among others it is the fastest one and consumes the least memory.

User avatar
tellu-white
Fanatic
Fanatic
Posts: 191
Joined: 2022-03-08, 22:02

Re: Querying if a URI was visited?

Unread post by tellu-white » 2025-05-15, 17:49

The code below is a version that can extract more information from History about the URL being searched.

EDIT: Corrected code to avoid error if the URL being searched does not exist in History:

Code: Select all

function query_History(page_URL_decoded) {
	try{
		var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
		var uri = ios.newURI(page_URL_decoded, null, null);

		var historyService = Components.classes["@mozilla.org/browser/nav-history-service;1"].getService(Components.interfaces.nsINavHistoryService);
		var query = historyService.getNewQuery();

		query.uri = uri;
		var options = historyService.getNewQueryOptions();

		// execute the query
		var result = historyService.executeQuery(query, options);

		// get results
		result.root.containerOpen = true;
		
		var count = result.root.childCount;
		
		if(count > 0){
			var node = result.root.getChild(0);
			
			// get node properties
			
			var title = node.title;
			var url = decodeURIComponent(node.uri);
			var access_count = node.accessCount;
			
			var lastVisitedTimeInMicrosecs = node.time;
			var lastVisitedTimeInMillisecond = parseInt(lastVisitedTimeInMicrosecs / 1000);		
			var date_and_time = new Date(lastVisitedTimeInMillisecond);
			
			var icon_url;
			var iconURI = node.icon; 					// is null if no favicon available
			if(iconURI){
				// alert(iconURI);						// moz-anno:favicon:https://forum.palemoon.org/favicon.ico
				var re = new RegExp('http.*', "i");
				icon_url = iconURI.match(re);			// return null if no matches are found
			} else{
				icon_url = iconURI;
			}
			
			var arr_return = [title, url, access_count, date_and_time, icon_url];

			result.root.containerOpen = false;
			
			return arr_return;
			
		} else{
			var arr_return = ["", "", "", "", ""];

			result.root.containerOpen = false;
			
			return arr_return;
		}
		
	} catch(err){
		alert(err.message);
	}
}

var page_URL = "https://forum.palemoon.org/";
var page_URL_decoded = decodeURIComponent(page_URL);

var arr_return = query_History(page_URL_decoded);

var title 			= arr_return[0];
var url 			= arr_return[1];
var access_count 	= arr_return[2];
var date_and_time 	= arr_return[3];
var icon_url 		= arr_return[4];

/*
alert('Title = ' + title);
alert('URL = ' + url);
alert('Number of Entries = ' + access_count);
alert('Last Visited ( Date and Time ) = ' + date_and_time);
alert('Icon URL = ' + icon_url);
*/

alert('\nTitle = ' + title + '\n\nURL = ' + url + '\n\nNumber of Entries = ' + access_count + '\n\nLast Visited ( Date and Time ) = ' + date_and_time + '\n\nIcon URL = ' + icon_url + '\n');
Result:

Title = Pale Moon forum - Forum index
URL = https://forum.palemoon.org/
Number of Entries = 5
Last Visited ( Date and Time ) = Thu May 15 2025 20:20:15 GMT+0300 (GTB Standard Time)
Icon URL = https://forum.palemoon.org/favicon.ico
01.png
You do not have the required permissions to view the files attached to this post.

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-16, 07:02

_yup_ wrote:
2025-05-15, 16:52
It's rather "just for the completeness of the collection".
But sometimes this is the only way to get the desired result.
Ah, in this sense I agree with you.
_yup_ wrote:
2025-05-15, 16:52
And in any case, every other method use a similar query at the end of call chain. How can one be sure that nsINavHistoryQuery or getPageTitle is not prone to such injection?
You can see how these functions are built. In any case, the nsIURI object will sanitize the url and encode the double quotes.

User avatar
_yup_
Moongazer
Moongazer
Posts: 13
Joined: 2025-04-26, 11:45

Re: Querying if a URI was visited?

Unread post by _yup_ » 2025-05-16, 18:23

Kris_88 wrote:
2025-05-16, 07:02
You can see how these functions are built.
When I was starting creation of the Stargazer extension, I knew almost nothing about Places API. My initial hope was to substitute the standard SQL query string with my own one somewhere inside or under the nsINavHistoryQuery. Attempting to find the right place, I traced all its call chain down to the point where all nsINavHistoryQuery's input parameters are passed to the native (compiled) code in almost their original form. So all real job (including building the SQL query string) is done by the C++ code, and (as for me) enforcing of an average extensions developer to study the whole browser sources is "a little too much".
In any case, the nsIURI object will sanitize the url and encode the double quotes.
... but leave single quotes intact.

Kris_88
Board Warrior
Board Warrior
Posts: 1101
Joined: 2021-01-26, 11:18

Re: Querying if a URI was visited?

Unread post by Kris_88 » 2025-05-16, 18:46

_yup_ wrote:
2025-05-16, 18:23
and (as for me) enforcing of an average extensions developer to study the whole browser sources is "a little too much".
The problem is that we don't have up-to-date documentation that reflects the current state of the browser. So the only adequate documentation is the browser source code. Sometimes I have to dive in there if nothing else helps...