I am writing a console application in which I am using Nreco.PhantomJS to load the data. This is a scrapping application for a website containing google map with markers on it. The data I am trying to get is not available on the page before or after it is loaded completely. The data is the simple Latitude and Longitude that is passed from a JavaScript function to the google map and is not rendered on the page. As I am very new to PhantomJS so I am not sure how to achieve this but I am sure this can be done. This small piece of script is run by me in c# code,
try
{
string jsContent = "var system = require('system');" +
"var page = require('webpage').create();" +
"page.open('" + url + "', function(status) {" +
"system.stdout.writeLine(GetLatLang());" +
"}"+
"phantom.exit();" +
"});";
phantomJS.RunScript(jsContent, null, null, ms);
}
finally
{
phantomJS.Abort(); // ensure that phantomjs.exe is stopped
}
When I call Alert(GetLatLang()) function in console tab (Inside google chrome inspector) then it run fines and value is retrieved. However the PhantomJS never finishes running the code and I have to close the application. My understanding is that in the above code PhantomJS immediately try to execute the GetLatLang() function whereas it is not available at that time. Is there any way to execute this function after the page is completely loaded?
You need to call the GetLatLang() function in the page context and not in the phantom context:
"page.open('" + url + "', function(status) {" +
"console.log(page.evaluate(function(){" +
"return GetLatLang();" +
"}));" +
"}"+
page.evaluate() is the only function that provides access to the DOM and the page's window object.
Additionally, your JavaScript has a syntax error. There is only one opening {, but two closing }. You need to remove "}"+.
Related
I am just getting started with CEF4Delphi. I have a chromium window on a tab in a page control. There are javascript hooks attached to elements on the webpage which register clicks and fire off native delphi code. This is all working correctly on one webpage. I can switch tabs and keep clicking on the webpage and receiving the click events.
However, when loading a different webpage there is some strange behaviour. The first time the tab is loaded clicks will be registered just fine. The issue arises when switching tabs. The second time a tab is loaded there are no clicks being registered.
This second webpage is being created by blazor which is different to the first but I am not sure why that would be affecting it.
I have tested using Google.com and it works fine on there also.
Chromium1.LoadURL('localhost:6064');
is run when the tab is loaded.
class procedure TmyExtension.ButtonClick(const data: string);
var
msg: ICefProcessMessage;
begin
msg := TCefProcessMessageRef.New('ButtonClick');
TCefv8ContextRef.Current.Browser.MainFrame.SendProcessMessage(PID_BROWSER, msg);
end;
The delphi code being fired
Chromium1.browser.MainFrame.ExecuteJavaScript(
'document.getElementById("'+
'NewButton' +
'").addEventListener("click", function(evt){' +
'function getpath(n){var ret = n.id;' +
'if (n.parentNode){return "" + ret}' +
'else return ret};'
'myextension.ButtonClick(getpath(evt.target))' +
'})', Chromium1.browser.MainFrame.GetURL, 0);
The Javascript being executed
procedure TtabWebPage.Chromium1ProcessMessageReceived(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame;
sourceProcess: TCefProcessId; const message: ICefProcessMessage;
out Result: Boolean);
begin
if (message = nil) or (message.ArgumentList = nil) then exit;
// This function receives the messages with the JavaScript results
if (message.Name = 'ButtonClick') then begin
Inc(FCount);
Result := True;
end;
inherited;
end;
Receiving the chromium message and increasing a count.
There doesn't seem to be a way to debug whether the javascript is being run. The ExecuteJavaScript function is definitely being run every time the page loads. I am wondering if there is anything to do with the blazor aspect of the webpage that causes this, or if there is any way to debug the issue.
Let me state initially that the code below works fine when I open a new browser page and enter my web server's URL, and it works also when I reload a page (F5 or Ctrl-R).
It only works partially however if I reopen a closed browser window and the browser restores my old tabs: then, the today's date is updated and displayed (see the code below, very simple), but the getJSON() call doesn't seem to be executed. The browser keeps displaying the data from the previous session. Only if I update the page (F5), the data in the browser window is updated.
I'm sure it's not the server. What else could it be?
Browsers: Firefox, Chrome, latest versions.
Using this code in index.html
<script>
$(document).ready(onReady());
</script>
and this code in helper.js
var onReady = function() {
// Display current date
var dateText = moment().format("dddd, Do MMMM YYYY");
var dateHTML = "<h2>" + dateText + "</h2>";
$("#date-col").append(dateHTML);
:
:
// Retrieve data from server and display it
var statText = "Statistics:";
$.getJSON("courses/starter", function(data) {
statText = statText.concat(" " + data.count + " starter");
$.getJSON("courses/dessert", function(data) {
statText = statText.concat(", " + data.count + " dessert");
$("#statistics").text(statText);
});
});
}
You have to pass a callback instead of executing the function immediately.
Try:
<script>
$(document).ready(onReady);
</script>
$( document ).ready(handler) expects argument to be a function expression which will be executed later when DOM is ready
In your example, you are invoking onReady() function and that function is not returning anything(undefined) hence exection will be like: $(document).ready(undefined);
Either use function expression like $(document).ready(onReady); or onReady() should return a function to be executed later.
Shorthand would be $(onReady)
I'm developing an extension for Google Chrome and the problem I'm having is I need to be able to call a JavaScript function that belongs to the webpage that's opened in the tab.
For details, the website is my website, therefore I know that function does exist. That function does a lot of things based on a string value. I want the user to be able to highlight text on any webpage, click a button from the Chrome extension that automatically loads my webpage and calls that function with the highlighted text as it's value.
Here's what I got so far:
chrome.tabs.create({ url: "https://mywebsite.com" }, function (tab) {
var c = "initPlayer('" + request.text + "');"; ////'request.text' is the highlighted text which works
chrome.tabs.executeScript(tab.id, { code: c });
});
But Chrome console says: "Uncaught ReferenceError: initPlayer is not defined."
I know that function does exist as it is in my code on my own website.
Any help is highly appreciated. Thanks!
This happens because pages and content scripts run inside two separate javascript contexts. This means that content scripts cannot acces functions and variables inside a page directly: you'll need to inject a script into the page itself to make it work.
Here is a simple solution:
chrome.tabs.create({url: "https://mywebsite.com"}, function (tab) {
var c = "var s = document.createElement('script');\
s.textContent = \"initPlayer('" + request.text + "');\";\
document.head.appendChild(s);"
chrome.tabs.executeScript(tab.id, {code: c});
});
NOTE: since January 2021, use Manifest V3 with chrome.scripting.executeScript() instead of chrome.tabs.executeScript().
With the above code you will basically:
Create the tab
Inject the code (variable c) into it as a content script that will:
Create a script with the code you want to execute on the page
Inject the script in the page and, therefore, run its code in the page context
I am attempting to use JSOM to create a sub-site (web) on button-click. The function is working - in so far as a site is created with the given configuration. However, despite the fact that a site is indeed being create, the ExecuteQueryAsync call always routes to the failure function. The relevant code is below:
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var webCreationInfo = new SP.WebCreationInformation();
webCreationInfo.set_title(portalName); //note: this variable is defined elsewhere
webCreationInfo.set_description('');
webCreationInfo.set_language(1033);
webCreationInfo.set_url(webUrl); //note: this variable is defined elsewhere
webCreationInfo.set_useSamePermissionsAsParentSite(false);
webCreationInfo.set_webTemplate(templateGuid); //note: this variable is defined elsewhere
web.get_webs().add(webCreationInfo);
web.update();
ctx.load(web);
ctx.executeQueryAsync(
function() {
alert("Created Site");
},
function(sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
);
This includes a couple variables that are defined elsewhere in my code, but this should not be a part of my problem. The site is properly created, using the correct template, name, and URL.
The 'Request failed' alert is always what pops up for me, despite the site being created correctly. The value of args.get_message() is "Unexpected response from server" and the value of args.get_stackTrace() is 'null'.
The issue here was actually due to the button and not the above code. I needed to add 'event.preventDefault();' to the start of my onClick function. Otherwise, the page attempts to refresh - which happens much faster than the site can be provisioned.
I was wondering if it was possible to intercept and control/redirect DNS requests made by Firefox?
The intention is to set an independent DNS server in Firefox (not the system's DNS server)
No, not really. The DNS resolver is made available via the nsIDNSService interface. That interface is not fully scriptable, so you cannot just replace the built-in implementation with your own Javascript implementation.
But could you perhaps just override the DNS server?
The built-in implementation goes from nsDNSService to nsHostResolver to PR_GetAddrByName (nspr) and ends up in getaddrinfo/gethostbyname. And that uses whatever the the system (or the library implementing it) has configured.
Any other alternatives?
Not really. You could install a proxy and let it resolve domain names (requires some kind of proxy server of course). But that is a very much a hack and nothing I'd recommend (and what if the user already has a real, non-resolving proxy configured; would need to handle that as well).
You can detect the "problem loading page" and then probably use redirectTo method on it.
Basically they all load about:neterror url with a bunch of info after it. IE:
about:neterror?e=dnsNotFound&u=http%3A//www.cu.reporterror%28%27afew/&c=UTF-8&d=Firefox%20can%27t%20find%20the%20server%20at%20www.cu.reporterror%28%27afew.
about:neterror?e=malformedURI&u=about%3Abalk&c=&d=The%20URL%20is%20not%20valid%20and%20cannot%
But this info is held in the docuri. So you have to do that. Here's example code that will detect problem loading pages:
var listenToPageLoad_IfProblemLoadingPage = function(event) {
var win = event.originalTarget.defaultView;
var docuri = window.gBrowser.webNavigation.document.documentURI; //this is bad practice, it returns the documentUri of the currently focused tab, need to make it get the linkedBrowser for the tab by going through the event. so use like event.originalTarget.linkedBrowser.webNavigation.document.documentURI <<i didnt test this linkedBrowser theory but its gotta be something like that
var location = win.location + ''; //I add a " + ''" at the end so it makes it a string so we can use string functions like location.indexOf etc
if (win.frameElement) {
// Frame within a tab was loaded. win should be the top window of
// the frameset. If you don't want do anything when frames/iframes
// are loaded in this web page, uncomment the following line:
// return;
// Find the root document:
//win = win.top;
if (docuri.indexOf('about:neterror') == 0) {
Components.utils.reportError('IN FRAME - PROBLEM LOADING PAGE LOADED docuri = "' + docuri + '"');
}
} else {
if (docuri.indexOf('about:neterror') == 0) {
Components.utils.reportError('IN TAB - PROBLEM LOADING PAGE LOADED docuri = "' + docuri + '"');
}
}
}
window.gBrowser.addEventListener('DOMContentLoaded', listenToPageLoad_IfProblemLoadingPage, true);