Webextension: cannot paste text from clipboard to input element - firefox-addon-webextensions

I'm writing a WebExtension for Firefox. In it, I want to be able to copy arbitrary text between the webextension and the clipboard. As far as I could see from the documentation (https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard), there is no way to transfer data between a javascript variable and the clipboard, it seems I need to take a detour through a DOM element.
For copying text into the clipboard, I've come up with a dummy method, which is very similar to what is described in this question: Firefox webextension not copying to clipboard
function copy(contentToCopy) {
var txtToCopy = document.createElement('input');
txtToCopy.style.left = '-300px';
txtToCopy.style.position = 'absolute';
txtToCopy.value = contentToCopy;
document.body.appendChild(txtToCopy);
txtToCopy.select();
console.log("Copying ", txtToCopy.value);
var res = document.execCommand('copy');
console.log("Copy result ", res);
txtToCopy.parentNode.removeChild(txtToCopy);
}
I can then call it with
copy('any arbitrary text');
and it works perfectly.
However, I also need to access the clipboard contents in the same way, and can't get it to work:
function paste() {
var txtToPaste = document.createElement('input');
txtToPaste.style.left = '-300px';
txtToPaste.style.position = 'absolute';
txtToPaste.value = 'dummy content for debugging';
document.body.appendChild(txtToPaste);
txtToPaste.focus();
txtToPaste.select();
var res = document.execCommand('paste');
var result = txtToPaste.value;
console.log("Paste result ", res);
console.log('Pasted text', result);
console.log('txtToPaste', txtToPaste);
txtToPaste.parentNode.removeChild(txtToPaste);
return result;
}
I have also requested the appropriate permission in my manifest.json file:
"permissions": ["clipboardRead" ]
I then try to call the method like this:
var dataFromClipboard = paste();
However, no matter what data I have in my clipboard when I call the method, the "Paste result" is always "true" and "result" is "dummy content for debugging" (i.e. unchanged from what I used to initialise the dummy field).
I'm testing this with Firefox 57.0.2 (64-bit) on Windows 7 (64-bit).
Am I missing something obvious? Why does this work in one direction but not the other?
The javascript console (neither in the tab in which the extension is being tested nor in the global browser console) is not showing any errors.

After another look at https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard#Reading_from_the_clipboard I saw the section titled "Browser-specific considerations". I don't know why I missed it on my first read through, but it provides the solution:
Firefox supports the "clipboardRead" permission from version 54, but
does require an element in content editable mode, which for content
scripts only works with a <textarea>.
So with this knowledge I have modified my function as follows:
function paste() {
var txtToPaste = document.createElement('textarea');
txtToPaste.id = "txtToPaste";
txtToPaste.style.left = '-300px';
txtToPaste.style.position = 'absolute';
txtToPaste.contentEditable = true;
txtToPaste.textContent = '';
document.body.appendChild(txtToPaste);
txtToPaste.focus();
txtToPaste.select();
var res = document.execCommand('paste');
var result = txtToPaste.textContent;
console.log("Copy result ", res);
console.log('Pasted text', result);
console.log('txtToPaste', txtToPaste);
txtToPaste.parentNode.removeChild(txtToPaste);
return result;
}
With these changes (changing input to textarea; setting contentEditable to true) the method works as I had hoped.

Related

Extjs 7.0 Calendar headers not aligned with columns

Using the Calendar component in Extjs 7.0 we noticed that the header cells didn't line up correctly with their columns if the language was set to Dutch:
When checking the source code I found the place where these values are added in the cell html;
In Ext.calendar.header.Base, in the setHeaderText function the following code exists:
var me = this,
D = Ext.Date,
value = me.getValue(),
format = me.getFormat(),
domFormat = me.domFormat,
cells = me.cells,
len = cells.length,
useDates = me.useDates,
cell, i;
if (!value) {
return;
}
value = D.clone(value);
for (i = 0; i < len; ++i) {
cell = cells[i];
if (useDates) {
cell.setAttribute('data-date', D.format(value, domFormat));
}
cell.setAttribute('data-day', value.getDay());
cell.innerHTML = D.format(value, format);
value = D.add(value, D.DAY, 1);
}
The innerHtml is set by formatting the Date(D) object which results in the 3 characters of that day. If you change this to just setting a 4 char value like cell.innerHTML = 'Test' the headers line up just fine:
But for some reason this doesn't work when using the D.format value. If somebody has any idea what causes this, I would love to hear.
I can't seem to test if this also goes wrong in another language cause for some reason my packages can't be loaded in anymore.
You can set(or fix) localization override localization constants.
Calendar use Ext.Date.dayNames and Ext.Date.getShortDayName().
All constants list you can see in localization package.
Fiddle example

Cannot get local IP address in ReactJS

I'm create a website using ReactJS and I want get my local IP address. I've tried few ways to do that but I was confused with result.
First, I use module on npm is local-ip-url, then use localIpUrl() to get my ip address and display with console.log(). If i use a terminal to run this .js file (eg: node index.js), it print exact result i want (192.168.x.x). But if i use browser to see console log, it shows a different result 127.0.0.0.
I've tried another ways, create my own function:
const os = require('os');
function getAvailableIp() {
let ifaces = os.networkInterfaces();
let result = {};
for (let ifname in ifaces) {
ifaces[ifname].forEach(function (iface) {
if ('IPv4' !== iface.family || iface.internal !== false) return;
result[ifname] = iface.address;
});
};
return result;
}
function getIp() {
let ips = getAvailableIp();
return ips.WiFi || 'localhost';
}
Same like above, when I ran code in terminal, it showed perfect result. But on browser, function os.networkInterfaces() return a empty object {}(It has a property is proto, i think it doesn't cause problem). So, it always return localhost.
Anyone can explain?

Loading a new window from local files and accessing it's contents

I am setting up a local webpage which shows videos in a HTML5 video tag. I just want to be able to do database search from a PHP request and show the results from which I can click on and show the video I want. The problem I have is that hte videos load WAY faster when loading from a "file:///" link than from the "http://" link. Server works flawlessly when in "HTTP" mode but nothing works in "file:///" mode which is normal as PHP codes only execute on the server side when requested to the server.
I have spent my full day trying soo much stuff. I changed my server to accept CORS, I tried window.open, storing the reference in a variable, local or global but I lose this as soon as I get out of my javascript function. I tried window.open in a function which is called from another function but no matter what I do, the window reference gets lost as soon as I leave the functions, or once the functions have finished. Since my browser is used as my main browser, I do not want to disable the security arround CORS but since my webpage's link comes from "file:///" requesting to "HTTP" on the same computer, CORS blocks me and wants an HTTP request which I can't give.
I have done all the searching for retrieving information from another webpage but I am always stuck with the "same domain" problem. I tried AJAX HTTPRequest, I just have no more solution for this simple problem which finished way more complicated than expected. The initial problem was just my videos not loading fast enough in HTTP mode (The speed difference is extreme, for 10 min videos, I can wait 5-10 seconds to skip through it while as in FILE:/// urls, It's almost instant, no waiting. longer videos of 1h, I can wait up to 20 and 30 seconds while as in file:/// mode, almost instant.) and I had to learn all that Allow cross domains stuff which ended up with no success either. I figure that maybe a few other heads may have better ideas than mine now.
#In my httpd.conf file from Apache
DocumentRoot "e:/mainwebfolder"
Alias "/lp" "d:/whatever"
//////////////////////////////////////
// index.php file that does not contain PHP contents
// window.location.href: file://d:/whatever/index.php
//////////////////////////////////////
<head>
<script src="html/servcom.js" type="text/javascript"></script>
</head>
<video id="vplayer" width="1280" height="720" controls></video>
<div id="search-form">
<input id="srch" name="srch" type="text">
<button class="bbut" onclick="ServInfo('search-results','http://127.0.0.1/lp/html/db.php','mode=s','search-form');">Search</button>
</div>
<div id='search-results'></div>
<script>
var dplay = document.getElementById("vplayer");
ShowVideo('MyVideo.mp4');
function ShowVideo (vidUrl) {
dplay = document.getElementById("vplayer");
dplay.src = vidUrl;
dplay.load;
}
</script>
//////////////////////////////////////
// Now this is in my javascript file servcom.js
//////////////////////////////////////
var win_ref = -1;
function ServInfo(pop_field_id,web_page,params="",form_id="",exec_string = "") {
var sparams = params;
var swpage = web_page;
var eobj = document.getElementById(pop_field_id);
var moreparams = "";
// If we entered extra parameters including form fields,
// add the the "&" before the form field list
if (sparams != "") {moreparams = "&";}
// Get form field values if a form id is specified
if (form_id != "") {
var efrm = document.getElementById(form_id);
sparams += moreparams+GetDivFields(form_id);
}
// Add the question mark if there is any parameters to pass
if (sparams != "") {
sparams = "?"+sparams;
// Add recieving objects reference
sparams += "&srco="+pop_field_id;
}
// If HTML element to populate does not exist, exit
if (typeof(eobj) == "!undefined" || eobj == null) {return;}
win_ref = window.open(swpage+sparams,"_blank");
//////////////////////////////////////
// right here win_ref will never be available once the code from this function has been finished executing although the variable is global. The problem starts here.
//////////////////////////////////////
// Execute a string if a user defined one
if (exec_string != "") {eval(exec_string);}
}
// Build a parameter string with div fields of type text, hidden or password
function GetDivFields(div_id) {
var ediv = document.getElementById(div_id);
var elem = ediv.children;
var retval = "";
var ssep = "";
for (var i = 0; i < elem.length; i++) {
if (elem[i].type == "text" || elem[i].type == "hidden" || elem[i].type == "password") {
retval += ssep+elem[i].name+"="+pURL(elem[i].value);
ssep = "&";
}
if (elem[i].type == "checkbox") {
if (elem[i].checked == true) {
retval += ssep+elem[i].name+"="+elem[i].value;
ssep = "&";
}
}
}
return retval;
}
//////////////////////////////////////
// And this is a brief overview of my db.php page
//////////////////////////////////////
<?php // Search Database code ?>
<div id="output"></div>
<script>
document.getElementById('output').innerHTML = "<?php echo $search_results; ?>";
// I actually want to retrieve the info from this div element once it has been populated from the initial page that called window.open for this page. BUT again. window.opener becomes empty once my initial window.open script finishes.
</script>
Access my newly loaded page's "output" div innerHTML OR loading videos through local HTTP as fast as "FILE:///".
Well, I fanally found a solution. Since this is for local and presentation use only, I could bypass some securities. Basically, doing what we would normally NOT do in a website but all this WITHOUT modifying your webserver config or touching any .htaccess file. Basically, no security restrictions, just a plain old hack that poses no security breaches for your browser or your server.
To be noted:
2 different websites exist (so 2 different folders at very different locations), 1 for developpement and serious releases, one for internal and/or presentation purposes.
Every file is local abd inside the presentation folder.
No PHP code can be ran from a "file:///" link.
Access to the mysql database is done through PHP and server is on Apach24
Reading video locally from a "file:///" link are WAY faster than from an "http://" link
Searching needs to be done in MySQL database frm a "http://" link and results need to be displayed on a webpage opened from a "file:///" link.
No changes must be made in the Browser's configuration so disabling CORS is not a solution.
Bypassing cors with methods proposed by many site won't work because of security reasons or because CORS bypass does not accept "file:///" links
PHP can write files on the server which is where I decided to bypass CORS. Since XML requests through AJAX can be done on the same origin domain an thus, purely in javascript. If a file exists which contains no PHP code AND resides on the same domaine i/e "file:///", the contents can the be read wothout any problems.
So I simply do the following in my db.php file:
$s_mode = "";
$s_text = "";
$sres = "";
if (isset($_REQUEST["srch"])) {$s_text=$_REQUEST["srch"];}
if (isset($_REQUEST["mode"])) {$s_mode=$_REQUEST["mode"];}
if ($s_mode == "s") {
$sres = SearchDB($s_text);
WriteFile("D:/whatever/my_path/dbres.html",$sres);
}
// Writes the contents of the search in a specified file
function WriteFile($faddress,$fcontents) {
$ifile = fopen($faddress,"w");
fwrite($ifile,$fcontents);
fclose($ifile);
}
Now using a normal AJAX request, I do 2 things. I opted to use an iframe with a "display:none" style to not bother seeing another tab openup.
Do the actual request which opens the "cross-doamin" link in the iframe WHICH executes my db.php code. I basically open "http://127.0.0.1/whatever/db.php?param1=data&parma2=data" inside my iframe.
Once my search is done and I have the results, my db.php will save an html file with the results as it's contents in my "file:///" direct location's path so: "D:/whatever/my_path/dbres.html".
I added a new function in my servcom.js. So my new file's contents looks like this:
// Show page info in another page element or window with parameters (for local use only)
function ServInfoLocal(dest_frame,web_page,params="",form_id="") {
var sparams = params;
var swpage = web_page;
var iweb = document.getElementById(dest_frame);
var moreparams = "";
// If we entered extra parameters including form fields,
// add the the "&" before the form field list
if (sparams != "") {moreparams = "&";}
// Get form field values if a form id is specified
if (form_id != "") {
var efrm = document.getElementById(form_id);
sparams += moreparams+GetDivFields(form_id);
}
// If destination frame does not exist, exit
if (typeof(iweb) == "!undefined" || iweb == null) {return;}
// Add the question mark if there is any parameters to pass
if (sparams != "") {sparams = "?"+sparams;}
// Show results in iframe
iweb.src = swpage+sparams;
}
// AJAX simple HTTP GET request
function ServInfo(pop_field_id,web_page,params="",form_id="",append_data_to_output = "",exec_string = "",dont_show_results = "") {
var sparams = params;
var swpage = web_page;
var eobj = document.getElementById(pop_field_id);
var moreparams = "";
// If we entered extra parameters including form fields,
// add the the "&" before the form field list
if (sparams != "") {moreparams = "&";}
// Get form field values if a form id is specified
if (form_id != "") {
var efrm = document.getElementById(form_id);
sparams += moreparams+GetDivFields(form_id);
}
// If HTML element to populate does not exist, exit
if (typeof(eobj) == "!undefined" || eobj == null) {return;}
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else {
// IE6-
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Do not show any results if requested
if (dont_show_results == "") {
if (append_data_to_output == "y") {
document.getElementById(pop_field_id).innerHTML += this.responseText;
}
if (append_data_to_output == "") {
document.getElementById(pop_field_id).innerHTML = this.responseText;
}
}
// Execute a string if a user defined one
if (exec_string != "") {
eval(exec_string);
}
}
};
// Add the question mark if there is any parameters to pass
if (sparams != "") {swpage += "?";}
xmlhttp.open("GET",swpage+sparams,true);
xmlhttp.send();
}
// Build a parameter string with div fields of type text, hidden or password
function GetDivFields(div_id) {
var ediv = document.getElementById(div_id);
var elem = ediv.children;
var retval = "";
var ssep = "";
for (var i = 0; i < elem.length; i++) {
if (elem[i].type == "text" || elem[i].type == "hidden" || elem[i].type == "password") {
retval += ssep+elem[i].name+"="+pURL(elem[i].value);
ssep = "&";
}
if (elem[i].type == "checkbox") {
if (elem[i].checked == true) {
retval += ssep+elem[i].name+"="+elem[i].value;
ssep = "&";
}
}
}
return retval;
}
Now, my dbres.html file will contain just the div elements and all the information I need to show up in my "file:///" page from which the search request came from. So I simply have this inside my page:
<div id="search-form" style="color:white;font-weight:bold;">
<input id="srch" name="srch" type="text">
<button class="bbut" onclick="ServInfoLocal('iweb','http://127.0.0.1/whatever/html/db.php','mode=s','search-form');">Search</button>
<button class="bbut" onclick="ServInfo('search-results','dbres.html');">Click here</button>
</div>
<div id="search-results">Results here</div>
<iframe id="iweb" style="display:none;" src=""></iframe>
For now I have 2 buttons, one for the search and one to show the results from my newly created file. Now, I can show my local videos which will load in my video container with "file:///" source directly without passing through http. I'll make my results display automatic which I will be able to do myself from here on.
So, if someone on planet earth wants to be able to do cross-domain searches in a MySQL database from a local file ran directly from the Windows explorer, there's not too many solutions, actually, I found none so here is at least one for who would ever need this solution.
For the curious ones out there, my next step will be to loop my folder until my dbres file is present using another js function. Once my file has been fetched, call another php file which wil destroy the created file and I'll be ready for another database request from my webpage situated in a "file:///" location.

modifying object in componentDidMount() [duplicate]

I’ll start with the code:
var s = ["hi"];
console.log(s);
s[0] = "bye";
console.log(s);
Simple, right? In response to this, the Firefox console says:
[ "hi" ]
[ "bye" ]
Wonderful, but Chrome’s JavaScript console (7.0.517.41 beta) says:
[ "bye" ]
[ "bye" ]
Have I done something wrong, or is Chrome’s JavaScript console being exceptionally lazy about evaluating my array?
Thanks for the comment, tec. I was able to find an existing unconfirmed Webkit bug that explains this issue: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: now fixed!)
There appears to be some debate regarding just how much of a bug it is and whether it's fixable. It does seem like bad behavior to me. It was especially troubling to me because, in Chrome at least, it occurs when the code resides in scripts that are executed immediately (before the page is loaded), even when the console is open, whenever the page is refreshed. Calling console.log when the console is not yet active only results in a reference to the object being queued, not the output the console will contain. Therefore, the array (or any object), will not be evaluated until the console is ready. It really is a case of lazy evaluation.
However, there is a simple way to avoid this in your code:
var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());
By calling toString, you create a representation in memory that will not be altered by following statements, which the console will read when it is ready. The console output is slightly different from passing the object directly, but it seems acceptable:
hi
bye
From Eric's explanation, it is due to console.log() being queued up, and it prints a later value of the array (or object).
There can be 5 solutions:
1. arr.toString() // not well for [1,[2,3]] as it shows 1,2,3
2. arr.join() // same as above
3. arr.slice(0) // a new array is created, but if arr is [1, 2, arr2, 3]
// and arr2 changes, then later value might be shown
4. arr.concat() // a new array is created, but same issue as slice(0)
5. JSON.stringify(arr) // works well as it takes a snapshot of the whole array
// or object, and the format shows the exact structure
You can clone an array with Array#slice:
console.log(s); // ["bye"], i.e. incorrect
console.log(s.slice()); // ["hi"], i.e. correct
A function that you can use instead of console.log that doesn't have this problem is as follows:
console.logShallowCopy = function () {
function slicedIfArray(arg) {
return Array.isArray(arg) ? arg.slice() : arg;
}
var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray);
return console.log.apply(console, argsSnapshot);
};
For the case of objects, unfortunately, the best method appears to be to debug first with a non-WebKit browser, or to write a complicated function to clone. If you are only working with simple objects, where order of keys doesn't matter and there are no functions, you could always do:
console.logSanitizedCopy = function () {
var args = Array.prototype.slice.call(arguments);
var sanitizedArgs = JSON.parse(JSON.stringify(args));
return console.log.apply(console, sanitizedArgs);
};
All of these methods are obviously very slow, so even more so than with normal console.logs, you have to strip them off after you're done debugging.
This has been patched in Webkit, however when using the React framework this happens for me in some circumstances, if you have such problems just use as others suggest:
console.log(JSON.stringify(the_array));
Looks like Chrome is replacing in its "pre compile" phase any instance of "s" with pointer to the actual array.
One way around is by cloning the array, logging fresh copy instead:
var s = ["hi"];
console.log(CloneArray(s));
s[0] = "bye";
console.log(CloneArray(s));
function CloneArray(array)
{
var clone = new Array();
for (var i = 0; i < array.length; i++)
clone[clone.length] = array[i];
return clone;
}
the shortest solution so far is to use array or object spread syntax to get a clone of values to be preserved as in time of logging, ie:
console.log({...myObject});
console.log([...myArray]);
however be warned as it does a shallow copy, so any deep nested non-primitive values will not be cloned and thus shown in their modified state in the console
This is already answered, but I'll drop my answer anyway. I implemented a simple console wrapper which doesn't suffer from this issue. Requires jQuery.
It implements only log, warn and error methods, you will have to add some more in order for it to be interchangeable with a regular console.
var fixedConsole;
(function($) {
var _freezeOne = function(arg) {
if (typeof arg === 'object') {
return $.extend(true, {}, arg);
} else {
return arg;
}
};
var _freezeAll = function(args) {
var frozen = [];
for (var i=0; i<args.length; i++) {
frozen.push(_freezeOne(args[i]));
}
return frozen;
};
fixedConsole = {
log: function() { console.log.apply(console, _freezeAll(arguments)); },
warn: function() { console.warn.apply(console, _freezeAll(arguments)); },
error: function() { console.error.apply(console, _freezeAll(arguments)); }
};
})(jQuery);

Variable array/object in one file changes when you change it in a callback in another file

I have two files in Node.js where one requires the other one.
variable_test.js:
TEST = require('./variable_test_external.js');
TEST.get(function(myVariable) {
var changeMeVariable;
console.log(myVariable);
changeMeVariable = myVariable.epicVariable;
changeMeVariable.evenEpicerVariable = "test3";
TEST.get(function(myVariable2) {
console.log(myVariable2);
});
});
variable_test_external.js:
var testVariable = new Array({epicVariable: {evenEpicerVariable: "test1"}}, {epicVariable: {evenEpicerVariable: "test2"}});
exports.get = function(callback) {
callback(testVariable[1]); // I know that the return is unnecessary in this example but in my real application I have return there for compactness.
}
This is the output when run in Node.js with node variable_test.js:
{ epicVariable: { evenEpicerVariable: 'test2' } }
{ epicVariable: { evenEpicerVariable: 'test3' } }
The console.log(myVariable) changes in the two TEST.get's. Why does this happen?
This is a reference copy, not a value copy. You got the object from the array, NOT a copy of them.
changeMeVariable = myVariable.epicVariable;
This would have to fix yout problem
// PSEUDO CODE, i don't know the correct syntax
changeMeVariable = {
epicVariable = myVariable.epicVariable
};
The answer in my case is the following based on the links at the bottom:
changeMeVariable = JSON.parse(JSON.stringify(myVariable.epicVariable));
But, it's much better to manually copy it like the bottom most link like this:
changeMeVariable = {
evenEpicerVariable: myVariable.epicVariable.evenEpicerVariable
}
n0m's answer is similar but if the epicVariable.evenEpicerVariable contained an object that object's reference would still be linked! (I tested it)
References:
What is the most efficient way to deep clone an object in JavaScript?
http://jsperf.com/cloning-an-object/3

Resources