I'm trying to set a HTML input to read-only using ExecuteScriptAsync. I can make it work, but it's not an ideal scenario, so I'm wondering if anyone knows why it doesn't work the way I would expect it to.
I'm using Cef3, version 63.
I tried to see if it's a timing issue and doesn't appear to be.
I tried invalidating the view of the browser but that doesn't seem to help.
The code I currently have, which works:
public void SetReadOnly()
{
var script = #"
(function(){
var labelTags = document.getElementsByTagName('label');
var searchingText = 'Notification Initiator';
var found;
for (var i=0; i<labelTags.length; i++)
{
if(labelTags[i].textContent == searchingText)
{
found = labelTags[i]
break;
}
}
if(found)
{
found.innerHTML='Notification Initiator (Automatic)';
var input;
input = found.nextElementSibling;
if(input)
{
input.setAttribute('readonly', 'readonly');
}
}})()
";
_viewer.Browser.ExecuteScriptAsync(script);
_viewer.Browser.ExecuteScriptAsync(script);
}
now, if I remove
found.innerHTML='Notification Initiator (Automatic)';
the input is no longer shown as read-only. The HTML source of the loaded webpage does show it as read-only, but it seems like the frame doesn't get re-rendered once that property is set.
Another issue is that I'm executing the script twice. If I run it only once I don't get the desired result. I'm thinking this could be a problem with V8 Context that is required for the script to run. Apparently running the script will create the context, so that could be the reason why running it twice works.
I have been trying to figure this out for hours, haven't found anything that would explain this weird behaviour. Does anyone have a clue?
Thanks!
Related
I am making an idle clicker game for fun, everything was going fine until I encountered a problem.
What I basically want to happen is when the image is clicked and the clickCounter element is over one, the new image element is created. No problem here, the main problem is saving the image. If the user refreshes the page, I want the created element to still be there. I have tried using outerHTML and following some other Stack Overflow forum questions but I could never get a proper solution to this certain problem. I have also tried localStorage and cookies but I believe I am using them wrong.
My code is below, my sololearn will be linked below, consisting of the full code to my project.
function oneHundThou() {
var countvar = document.getElementById("clickCounter")
if(document.getElementById("clickCounter").innerHTML > 1) {
alert("Achievement! 1 pat!")
var achievement1k = document.createElement("img");
// create a cookie so when the user refreshes the page, the achievement is shown again
document.cookie = "achievement1k=true";
achievement1k.src = "https://c.pxhere.com/images/f2/ec/a3fcfba7993c96fe881024fe21e7-1460589.jpg!d";
achievement1k.style.height = "1000px"
achievement1k.style.width = "1000px"
achievement1k.style.backgroundColor = "red"
document.body.appendChild(achievement1k);
oneHundThou = function(){}; // emptying my function after it is run once instead of using a standard switch statement
}
else {
return
}
}
oneHundThou();
I am aware that there is another post that is similar to this, however, my answer could not be answered on that post.
Full code is here: https://code.sololearn.com/Wwdw59oenFqB
Please help! Thank you. (:
Instead of storing the image, try storing the innerHTML, then create the image on document load:
function oneHundThou() {
countvar.innerHTML = localStorage.getItem('clickCount') ? localStorage.getItem('clickCount') : 0; //before if
//original code
localStorage.setItem('clickCount', countvar.innerHTML); //instead of doc.cookie
}
window.addEventListener('DOMContentLoaded', (event) => {
oneHundThou();
});
or, if you don't care that clickCounter may initialize to null, you can remove the ? : and just put
countvar.innerHTML = localStorage.getItem('clickCount');
Edit: shortened code with the countvar variable
Running in IE is a legacy app built with frames that makes alot of references cross-frame like parent.header.blah.blah and parent.sidebar.so.and.so. Worked fine in old IE compatibility mode. Works in chrome and edge (chromium).
But in regular IE without compatibility mode on, it's throwing a permission denied error on line 43. Thing is, it throws the error in the console NO MATTER WHAT IS ON LINE 43!!! I added superfluous lines of code to push other code down, took out code to move other code up. Doesn't matter, the console ALWAYS says it's on line 43.
I put breakpoints in and noticed the error doesn't actually add to the console until AFTER the javascript has finished running. The page is very large with ALOT of javascript, and it's dificult to comment a section out without breaking the page to experiment with what might be causing the permission denied.
Permission Denied is supposed to indicate a same-origin violation as I understand it, but all frames and files are coming through the same servlet on the same URL with only parameters changing. I printed out the document.domain of every frame, they all are identical.
So..I'm not even sure what to do at this point to narrow it down. How can I figure out what is really the offending piece of code...or even section?
UPDATE - So it seems that the error is actually coming from a function in another frame being called from this frame (nowhere near line 43 by the way). That function is managing the options in a select list. The actual error comes here:
for (var k=0; k < assetListz.options.length; k++) {
if (assetListz.options[k].value == currentAsset) { //permission denied!
inList = true;
assetListz.options[k].selected = true;
break;
}
}
assetListz didn't have a 'z' on it until I just did that to make sure I wasn't accidentally getting scope to some OTHER assetList. I can test the length of the assetList, but as soon as I check the value on that second line, kaboom. Ideas?
Update 2 -
I changed the code to get the assetlist in each reference. No storing it. Blows up in the same place.
for (var k=0; k < document.getElementById('assetList').options.length; k++) {
if (document.getElementById('assetList').options[k].value == currentAsset) {
inList = true;
document.getElementById('assetList').options[k].selected = true;
break;
}
}
Okay, I got this fixed. I'm not sure exactly WHY this fix works, though I can guess. It seems that by rewriting the method so that it avoids use of the options array on the select object, everything works fine. I reference it just once to get the length...which it allows. but if i try to get a specific option by assetList.options[i].anything, then I get permission denied.
I still think this is a bug in IE11's same-origin code, but lucky for me, it seems like MS didn't re-use code so the same bug didn't 'protect' all means of accessing the select options. Just via the array property. Or maybe something else is goin on. I just know this worked for me.
//By changing the value attribute, we change the current selection.
assetList.value = currentAsset.toUpperCase();
if(assetList.selectedIndex == -1) {
//this means the current asset wasn't in the list.
if(assetList.options.length >= 1000) {
//not allowed to add more than 1000. And if so, set the selectedIndex back
alert("The Active Asset List contains the maximum of 1000 entries. \n" +
"The current Asset ID '" + currentAsset + "' was not added to the Active Asset List.");
assetList.selectedIndex = currentSelectedIndex;
return;
} else {
var option = document.createElement("OPTION");
option.value = currentAsset;
option.text = currentAsset;
assetList.add(option, 0);
assetList.selectedIndex = 0;
}
}
I spent way too much time trying to understand the problem here. I am working with a HID Barcode Scanner, and am able to get the device information. But I am unable to get a hold of the HidDevice object even with the right device id. It always return null. Here is what I have:
var selector = Windows.Devices.HumanInterfaceDevice.HidDevice.getDeviceSelector(parseInt('0x1', 16), parseInt('0x6', 16));
Windows.Devices.Enumeration.DeviceInformation.findAllAsync(selector, null).then(
function (deviceInfoCollection) {
if (deviceInfoCollection.length > 0) {
for (var i = 0; i < deviceInfoCollection.length; i++) {
var id = deviceInfoCollection.getAt(i).id;
return Windows.Devices.HumanInterfaceDevice.HidDevice.fromIdAsync(id, Windows.Storage.FileAccessMode.readWrite);
}
}
else {
throw "No Devices Discovered.";
}
})
.done(function (device) {
if (device != null)
successCallback(device.name);
});
I added these device capabilities in my manifest file:
<DeviceCapability Name="humaninterfacedevice">
<Device Id="any">
<Function Type="usage:0001 *"/>
</Device>
</DeviceCapability>
I'm going through the same issue now. The only thing I see in your code that strikes me as odd is the following manifest tag:
<Device Id="any">
Usually, the "any" value works. But I've had issues arise where the vendor and product id are required; I'm not quite sure why, but I think it's based off the type of device/usageid. I would try hardcoding the vendor and product id's to see if it makes a difference.
Another thought: I'm guessing by the usage tag that your scanner is configured as a keyboard. You can check to see if your scanner can be configured as a non-keyboard HID device, which helped me personally. I see other people on the internet having issues where an HidDevice is returned as null because another program is using that device; in your case, the OS might already be using the keyboard and locking it out somehow.
Best of luck!
I am debugging a javascript/html5 web app that uses a lot of memory. Occasionally I get an error message in the console window saying
"uncaught exception: out of memory".
Is there a way for me to gracefully handle this error inside the app?
Ultimately I need to re-write parts of this to prevent this from happening in the first place.
You should calclulate size of your localStorage,
window.localStorage is full
as a solution is to try to add something
var localStorageSpace = function(){
var allStrings = '';
for(var key in window.localStorage){
if(window.localStorage.hasOwnProperty(key)){
allStrings += window.localStorage[key];
}
}
return allStrings ? 3 + ((allStrings.length*16)/(8*1024)) + ' KB' : 'Empty (0 KB)';
};
var storageIsFull = function () {
var size = localStorageSpace(); // old size
// try to add data
var er;
try {
window.localStorage.setItem("test-size", "1");
} catch(er) {}
// check if data added
var isFull = (size === localStorageSpace());
window.localStorage.removeItem("test-size");
return isFull;
}
I also got the same error message recently when working on a project having lots of JS and sending Json, but the solution which I found was to update input type="submit" attribute to input type="button". I know there are limitations of using input type="button"..> and the solution looks weird, but if your application has ajax with JS,Json data, you can give it a try. Thanks.
Faced the same problem in Firefox then later I came to know I was trying to reload a HTML page even before setting up some data into local-storage inside if loop. So you need to take care of that one and also check somewhere ID is repeating or not.
But same thing was working great in Chrome. Maybe Chrome is more Intelligent.
I load this JS code from a bookmarklet:
function in_array(a, b)
{
for (i in b)
if (b[i] == a)
return true;
return false;
}
function include_dom(script_filename) {
var html_doc = document.getElementsByTagName('head').item(0);
var js = document.createElement('script');
js.setAttribute('language', 'javascript');
js.setAttribute('type', 'text/javascript');
js.setAttribute('src', script_filename);
html_doc.appendChild(js);
return false;
}
var itemname = '';
var currency = '';
var price = '';
var supported = new Array('www.amazon.com');
var domain = document.domain;
if (in_array(domain, supported))
{
include_dom('http://localhost/bklts/parse/'+domain+'.js');
alert(getName());
}
[...]
Note that the 'getName()' function is in http://localhost/bklts/parse/www.amazon.com/js. This code works only the -second- time I click the bookmarklet (the function doesn't seem to get loaded until after the alert()).
Oddly enough, if I change the code to:
if (in_array(domain, supported))
{
include_dom('http://localhost/bklts/parse/'+domain+'.js');
alert('hello there');
alert(getName());
}
I get both alerts on the first click, and the rest of the script functions. How can I make the script work on the first click of the bookmarklet without spurious alerts?
Thanks!
-Mala
Adding a <script> tag through DHTML makes the script load asynchroneously, which means that the browser will start loading it, but won't wait for it to run the rest of script.
You can handle events on the tag object to find out when the script is loaded. Here is a piece of sample code I use that seems to work fine in all browsers, although I'm sure theres a better way of achieving this, I hope this should point you in the right direction:
Don't forget to change tag to your object holding the <script> element, fnLoader to a function to call when the script is loaded, and fnError to a function to call if loading the script fails.
Bear in mind that those function will be called at a later time, so they (like tag) must be available then (a closure would take care of that normally).
tag.onload = fnLoader;
tag.onerror = fnError;
tag.onreadystatechange = function() {
if (!window.opera && typeof tag.readyState == "string"){
/* Disgusting IE fix */
if (tag.readyState == "complete" || tag.readyState == "loaded") {
fnLoader();
} else if (tag.readyState != "loading") {
fnError();
};
} else if (tag.readyState == 4) {
if (tag.status != 200) {
fnLoader();
}
else {
fnError();
};
};
});
It sounds like the loading of the external script (http://localhost/bklts/parse/www.amazon.com/js) isn't blocking execution until it is loaded. A simple timeout might be enough to give the browser a chance to update the DOM and then immediately queue up the execution of your next block of logic:
//...
if (in_array(domain, supported))
{
include_dom('http://localhost/bklts/parse/'+domain+'.js');
setTimeout(function() {
alert(getName());
}, 0);
}
//...
In my experience, if zero doesn't work for the timeout amount, then you have a real race condition. Making the timeout longer (e.g. 10-100) may fix it for some situations but you get into a risky situation if you need this to always work. If zero works for you, then it should be pretty solid. If not, then you may need to push more (all?) of your remaining code to be executed into the external script.
The best way I could get working: Don't.
Since I was calling the JS from a small loader bookmarklet anyway (which just tacks the script on to the page you're looking at) I modified the bookmarklet to point the src to a php script which outputs the JS code, taking the document.domain as a parameter. As such, I just used php to include the external code.
Hope that helps someone. Since it's not really an answer to my question, I won't mark this as the accepted answer. If someone has a better way, I'd love to know it, but I'll be leaving my code as is:
bookmarklet:
javascript:(function(){document.body.appendChild(document.createElement('script')).src='http://localhost/bklts/div.php?d='+escape(document.domain);})();
localhost/bklts/div.php:
<?php
print("
// JS code
");
$supported = array("www.amazon.com", "www.amazon.co.uk");
$domain = #$_GET['d']
if (in_array($domain, $supported))
include("parse/$domain.js");
print("
// more JS code
");
?>