I am developing a chrome extension where I am injecting a JavaScript script into the active tab when it loads. The code of the script I have attached below. When I use var for declaring myOptions and myGlobals objects, the script runs without any errors. But if I use let for declaring them, then I get syntax error on the first line stating that myOptions has already been declared. I have not even redeclared myOptions and myGlobals objects anywhere in my code. But I have tried to change the values of their properties. I am unable to figure out where I am going wrong. I want to know why let does not work in my code?
var myOptions = {
takeNotes:false,
displayNotes:false
}
var myGlobals = {
displayingForm:false,
tabUrl:window.location.href,
notesCache:[]
}
onloadForeground();
function onloadForeground(){
chrome.storage.sync.get(myGlobals.tabUrl, (data)=>{
myGlobals.notesCache = data[myGlobals.tabUrl]?data[myGlobals.tabUrl].savedNotes:[];
console.log(data);
myGlobals.notesCache.forEach(addSticker);
});
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log(request);
if (request.message === "change_takeNotes_option") {
console.log(`Changing take notes option to ${request.value}`);
myOptions.takeNotes = request.value;
sendResponse({
message:"success"
});
return true;
} else if (request.message === "change_displayNotes_option") {
console.log(`Changing display notes option to ${request.value}`);
myOptions.displayNotes = request.value;
displayNotes();
sendResponse({
message:"success"
});
return true;
} else if (request.message === "save_notes_cache") {
console.log("Saved notes");
saveNotes();
sendResponse({
message:"success"
});
return true;
} else if (request.message === "reset_notes") {
console.log("Reset notes");
resetNotes();
sendResponse({
message:"success"
});
return true;
}
});
function displayNotes(){
const notes = document.getElementsByClassName("note");
console.log(notes.length);
for (let i = 0; i < notes.length; i++) {
notes[i].style.visibility = (myOptions.displayNotes)?"visible":"hidden";
}
}
function saveNotes() {
if (myGlobals.notesCache.length > 0) {
chrome.storage.sync.set({[myGlobals.tabUrl]: {savedNotes:myGlobals.notesCache}});
} else {
chrome.storage.sync.remove(myGlobals.tabUrl);
}
}
function displayForm() {
myGlobals.displayingForm = true;
}
function discardForm() {
setTimeout(() => {
myGlobals.displayingForm = false;
}, 500);
}
function addNote(){
console.log("Adding note");
let noteTitle = document.getElementById("note-inputTitle").value;
let noteDescription = document.getElementById("note-inputDescription").value;
if (noteTitle == null || noteTitle.trim() === "") {
alert("The note requires a title");
} else if (noteDescription == null || noteDescription.trim() === "") {
alert("The note requires a description");
} else {
let note = {
title: noteTitle,
description: noteDescription,
}
myGlobals.notesCache.push(note);
console.log("Current note cache");
console.log(myGlobals.notesCache);
discardForm();
}
}
function discardNote(index) {
myGlobals.displayingForm=true;
setTimeout(()=>{
myGlobals.displayingForm=false;
}, 300);
console.log("Discarding note " + index);
myGlobals.notesCache.splice(index, 1);
console.log("Current note cache");
console.log(myGlobals.notesCache);
}
function resetNotes(){
myGlobals.notesCache = [];
console.log(notesCache);
}
This is the background script I am using to inject the above script
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
console.log(changeInfo);
if (changeInfo.status === "complete" && /^http/.test(tab.url)) {
chrome.scripting.insertCSS({
target: {
tabId: tabId
},
files: ["./foreground.css"]
})
chrome.scripting.executeScript({
target: {
tabId: tabId
},
files: ["./foreground.js"]
})
.then(() => {
console.log("Injected foreground script " + tabId);
chrome.storage.sync.set({ [tabId]: { options:{takeNotes:false, displayNotes:false} } });
})
.catch(err => {
console.log(err);
});
}
});
You use executeScript twice on the same page so when the injected script runs again it tries to declare a let variable in the same context, but this is forbidden by the JavaScript specification.
Solutions:
Keep using var
Wrap the code in an IIFE:
(() => {
// your entire code here
})()
Don't reinject the script twice by adding a condition before executeScript e.g. you can "ping" the tab:
// injected file
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg === 'ping') sendResponse(true);
});
// background or popup script
function inject(tabId) {
chrome.tabs.sendMessage(tabId, 'ping', {frameId: 0}, () => {
if (chrome.runtime.lastError) {
// ManifestV2:
chrome.tabs.executeScript(tabId, {file: 'content.js'});
// ManifestV3:
// chrome.scripting.executeScript({target: {tabId}, file: 'content.js'});
}
});
}
Try to check your HTML code. Maybe you included the Javascript code twice. That's the only explanation for the error. I can't see any other error in your code.
Related
I cannot manage to run WFS-T demos in GeoServer 2.17.2. In Demo always give an error. In a similar way, I want to run WPS demos but always got an error
coverage store not found
In the workspace I enabled WPS but I got no response coming from geoserver side.
HTTP response: 500 Server Error
Anyone know how can I run WFS-T demos in GeoServer ?
// Promises
var _eid_promises = {};
// Turn the incoming message from extension // into pending Promise resolving
window.addEventListener("message", function (event) {
if (event.source !== window) return;
if (event.data.src && (event.data.src === "background.js")) {
console.log("Page received: ");
console.log(event.data);
// Get the promise
if (event.data.nonce) {
var p = _eid_promises[event.data.nonce];
// resolve
if (event.data.result === "ok") {
if (event.data.signature !== undefined) {
p.resolve({
hex: event.data.signature
});
} else if (event.data.version !== undefined) {
p.resolve(event.data.extension + "/" + event.data.version);
} else if (event.data.cert !== undefined) {
p.resolve({
hex: event.data.cert
});
} else {
console.log("No idea how to handle message");
console.log(event.data);
}
} else { // reject
p.reject(new Error(event.data.result));
}
delete _eid_promises[event.data.nonce];
} else {
console.log("No nonce in event msg");
}
}
}, false);
function TokenSigning() {
function nonce() {
var val = "";
var hex = "abcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < 16; i++) val += hex.charAt(Math.floor(Math.random() * hex.length));
return val;
}
function messagePromise(msg) {
return new Promise(function (resolve, reject) { // amend with necessary metadata
msg["nonce"] = nonce();
msg["src"] = "page.js";
// send message
window.postMessage(msg, "*");
// and store promise
callbacks _eid_promises[msg.nonce] = {
resolve: resolve,
reject: reject
};
});
}
this.getCertificate = function (options) {
var msg = {
type: "CERT",
lang: options.lang,
filter: options.filter
};
console.log("getCertificate()");
return messagePromise(msg);
};
this.sign = function (cert, hash, options) {
var msg = {
type: "SIGN",
cert: cert.hex,
hash: hash.hex,
hashtype: hash.type,
lang: options.lang,
info: options.info
};
console.log("sign()");
return messagePromise(msg);
};
this.getVersion = function () {
console.log("getVersion()");
return messagePromise({
type: "VERSION"
});
};
}
I want to be able to send a message from my web app to my chrome extension so it can easier to use (send the auth token so users dont have to login twice). However after looking at the documentation and reading a bunch of SO questions, I cannot get anything working for me.
Here's some parts my manifest.json:
"content_security_policy": "script-src 'self' https://330218550995.ngrok.io; object-src 'self'",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"externally_connectable": {
"matches": [
"*://localhost/*",
"*://*.ngrok.io/*"
]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"exclude_matches": [
"*://*.olympiatasks.com/*",
"https://app.olympiatasks.com/*",
"https://*.olympiatasks.com/*"
],
"css": ["/static/css/main.css"],
"js": [
"/static/js/runtime-main.js",
"/static/js/2.js",
"/static/js/main.js"
]
}
],
Inside of the content script I do this:
const ExtensionID = process.env.REACT_APP_EXTENSION_ID || '';
chrome?.runtime.connect(ExtensionID, { name: 'example' });
chrome?.runtime?.sendMessage('Hi from content script')
Inside of the web page I do this:
const ExtensionID = process.env.REACT_APP_EXTENSION_ID || "";
chrome.runtime.connect(ExtensionID, { name: "example" });
chrome?.runtime?.sendMessage(ExtensionID, "Hi from app");
Then here is the listener in the background.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log({ request })
});
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
console.log("Received message from " + sender + ": ", request);
sendResponse({ received: true }); //respond however you like
});
When I open the website, the extension is successfully injected and in the dev console of the background.js page I get the following:
Hello world from background.js
{request: "Hi from content script"}
The "hi from app" is missing which means it's not being sent/received. I used ngrok to setup forwarding to my app thinking that either:
The domain being localhost
The protocol not being https
could be the problem but as you guess, no luck.
So far I have done the following:
Setup my externally_connectable inside my manifest.json
Setup the onMessageExternal listener in the background.js
Call runtime.sendMessage with the Extension ID like shown in the docs
Used an https website for secure connection
Any help on this issue is greatly appreciated
We can synchronize the authentication token between the website and chrome extension using content-script and background js file.
Inside web page I do this
...
I am not sure how you did it on the web page
Here is what I did to send access token from web page to extension.
Here is my content-script file.
/* eslint-disable no-undef */
const hostScript = () => {
// ----------------- Function Declarations -----------------
let listenGetTokenResponseFromWindow = () => {};
const sendMessagesToExtension = (msg, callback = null) => {
chrome.runtime.sendMessage(msg);
};
const sendMessagesToWindow = (msg, callback = null) => {
const { type } = msg;
switch (type) {
case ExtensionMessagesTypes.GetTokenFromWindow:
// Can not pass the function with window.postMessage. Only JSON object can be passed.
// So reset the listener
window.postMessage(msg, document.location.origin);
window.removeEventListener("message", listenGetTokenResponseFromWindow);
listenGetTokenResponseFromWindow = event => {
if (event.source !== window) return;
if (event.data.type === ExtensionMessagesTypes.GetTokenFromWindowResponse) {
const { payload } = event.data;
!!callback && callback(payload.token);
}
};
window.addEventListener("message", listenGetTokenResponseFromWindow);
break;
case ExtensionMessagesTypes.SetWindowToken:
window.postMessage(msg, document.location.origin);
!!callback && callback();
break;
default:
break;
}
};
const listenMessagesFromWindow = event => {
if (event.source !== window) return;
const msg = event.data;
Object.values(ExtensionMessagesTypes).includes(msg.type) && sendMessagesToExtension(msg);
};
const listenMessagesFromExtension = (msg, sender, response) => {
sendMessagesToWindow(msg, response);
return true; // means response is async
};
// ----------------- Listener Definitions -----------------
window.removeEventListener("message", listenMessagesFromWindow);
window.addEventListener("message", listenMessagesFromWindow);
chrome.runtime.onMessage.removeListener(listenMessagesFromExtension);
chrome.runtime.onMessage.addListener(listenMessagesFromExtension);
// Reset extension token as windows token
sendMessagesToWindow({ type: ExtensionMessagesTypes.GetTokenFromWindow }, token => {
sendMessagesToExtension({
type: ExtensionMessagesTypes.SetExtensionToken,
payload: { token }
});
});
};
Here is the background.js file.
(() => {
chrome.runtime.onMessage.addListener((msg, sender, response) => {
switch (msg.type) {
case ExtensionMessagesTypes.GetTokens:
getAccessTokens(response);
break;
case ExtensionMessagesTypes.SetExtensionToken:
!!msg.payload && !!msg.payload.token && setAccessTokenToExtensionLocalStorage(msg.payload.token, response);
break;
case ExtensionMessagesTypes.SetWindowToken:
!!msg.payload && !!msg.payload.token && sendMessageFromExtensionToWindow(msg, response);
break;
}
});
})();
const sendMessageFromExtensionToWindow = (msg, callback = null) => {
chrome.tabs.query({ currentWindow: true, url: `${HostURL}/*` }, tabs => {
if (tabs.length < 1) {
!!callback && callback(null);
return;
}
if (msg.type === ExtensionMessagesTypes.GetTokenFromWindow) {
chrome.tabs.sendMessage(tabs[0].id, msg, token => {
!!callback && callback(token);
});
} else if (msg.type === ExtensionMessagesTypes.SetWindowToken) {
chrome.tabs.sendMessage(tabs[0].id, msg, () => {
!!callback && callback();
});
}
});
};
// Authentication
const setAccessTokenToExtensionLocalStorage = (access_token, callback = null) => {
chrome.storage.local.set({ access_token }, () => {
!!callback && callback();
});
};
const getAccessTokenFromChromeStorage = callback => {
chrome.storage.local.get(['access_token'], result => {
!!callback && callback(result.access_token);
});
};
const getAccessTokens = callback => {
getAccessTokenFromChromeStorage(accessTokenFromExtension => {
sendMessageFromExtensionToWindow({ type: ExtensionMessagesTypes.GetTokenFromWindow }, accessTokenFromWindow => {
callback({
accessTokenFromExtension: accessTokenFromExtension || '',
accessTokenFromWindow: accessTokenFromWindow || ''
});
});
});
};
const handleLogin = payload => {
//TODO: Handling refresh token
const { token } = payload;
if (!token) return;
setAccessTokenToExtensionLocalStorage(token);
};
const handleLogout = () => {
setAccessTokenToExtensionLocalStorage(null);
};
PS: You don't need externally_connectable option in the manifest.json file.
Thanks to #wOxxOm comment I was able to resolve the issue.
I quote:
Tentatively, since ngrok.io is in public suffix list it means it's basically like com which is forbidden in externally_connectable. Try using a more specific pattern for the site.
I changed the URL to use one more specific https://330218550995.ngrok.io/* and it now works
I'm building an Electron application and I'm stuck with the following problem.
I'm getting information using a socket from an apparatus, and It was working fine. I wanted to change the html of the page if the program gets a type of message, so basically I used the loadUrl method, but then, after sending a message to the renderer process I't seems like it's not being received.
My code:
photoViewOn = false;
...
app.on('ready', function(){
// Create new window
mainWindow = new BrowserWindow({
backgroundColor: '#000000',
fullscreen : true,
frame : false,
icon : __dirname + "/res/logo.png",
webPreferences: {
nodeIntegration : true
}
});
mainWindow.webContents.openDevTools();
// Load html in window
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'bigNames.html'),
protocol: 'file:',
slashes:true,
}))...)
function HTMLupdate(msg) {
mainWindow && mainWindow.webContents.send('update', msg);
var server = socketBuilder('localhost', '7777', {
message: (msg, rinfo) => {
try {
console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
var infoMap = processCyrano(msg);
//if (infoMap["Com"] === "")
if (infoMap != null) {
if (infoMap["Com"] === "INFO") {
if (photoViewOn) {
photoViewOn = false;
bigNamesView();
}
console.log("Inside Infomap");
console.log(`Left Fencer: ${infoMap["LeftName"]}`);
console.log(`Right Fencer: ${infoMap["RightName"]}`);
HTMLupdate(infoMap);
}
}
}
catch (error) {
console.log(`Error ${error}`);
}
},
error: (err) => {
console.log(`server error:\n${err.stack}`);
server.close();
},
listen: () => {
const address = server.address();
console.log(`server listening ${address.address}:${address.port}`);
}
});
function photoView() {
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'photos.html'),
protocol: 'file:',
slashes:true,
}));
}
function bigNamesView() {
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'bigNames.html'),
protocol: 'file:',
slashes:true,
}));
}
function processCyrano(msg) {
try {
let stripMsg = msg.toString().split("%");
let info = {};
let compInfo = stripMsg[0].split("|");
console.log(compInfo);
if(compInfo[2] === "INFO" || compInfo[2] === "DISP") {
let firstFencerInfo = stripMsg[1].split("|")
let secondFencerInfo = stripMsg[2].split("|")
info.Protocol = compInfo[1];
info.Com = compInfo[2]
info.Piste = compInfo[3]
info.Compe = compInfo[4];
info.Phase = compInfo[5];
info.PoulTab = compInfo[6];
info.Match = compInfo[7];
info.Round = compInfo[8];
info.RightId = firstFencerInfo[1];
info.RightName = firstFencerInfo[2];
info.RightNat = firstFencerInfo[3];
info.Rscore = firstFencerInfo[4];
info.Rstatus = firstFencerInfo[5];
info.RYcard = firstFencerInfo[6];
info.Rrcard = firstFencerInfo[7];
info.Rlight = firstFencerInfo[8];
info.RWlight = firstFencerInfo[9];
info.LeftId = secondFencerInfo[1];
info.LeftName = secondFencerInfo[2];
info.LeftNat = secondFencerInfo[3];
info.Lscore = secondFencerInfo[4];
info.Lstatus = secondFencerInfo[5];
info.LYcard = secondFencerInfo[6];
info.Lrcard = secondFencerInfo[7];
info.Llight = secondFencerInfo[8];
info.LWlight = secondFencerInfo[9];
lastMatch = info;
return info;
}
else if (compInfo[2] === "PHOTO-NEXT") {
console.log("Photo-Next received");
photoViewOn = true;
photoView();
}
else if (compInfo[2] === "PHOTO-SCORE") {
console.log("Photo-score received");
photoViewOn = true;
photoView();
}
else if (compInfo[2] === "PHOTO-STOP") {
console.log("Photo-Stop received");
return lastMatch;
}
return null;
}
catch (error) {
//Avoid empty messages of the protocol
console.log(`Error ${error}`);
return null;
}
}
Basically my attempt is, if I get a "Photo-Score" message, call photoView() (this works fine), and if "Photo-Stop" is received, call bigNamesView() and start sending information agoin using HTMLUpdate(msg), but it doesn't work for me. Any clue why this is happening?
Note that I remove some irrelevant code. Thanks.
hi my friend you problem is that lastMatch = info only happens inside
if(compInfo[2] === "INFO" || compInfo[2] === "DISP") {
So Only When "Photo-Stop" is received then you return lastMatch
.
inside the code your logic says only when we receive INFO call bigNamesView()
but you want only when we receive PHOTO-STOP call bigNamesView()
.
Also about the loadurl method you call mainWindow.webContents.send('update', msg); in HTMLupdate immediately you should wait for event did-finish-load then call send update msg
win.webContents.on('did-finish-load', () => {
HTMLupdate(infoMap);
})
In your server you have the condition
if (photoViewOn) {bigNamesView()}
but in function processCyrano your condition for 'PHOTO-STOP' doesn't set photoViewOn to true.
else if (compInfo[2] === "PHOTO-STOP") {
console.log("Photo-Stop received");
return lastMatch;
}
Change to
else if (compInfo[2] === "PHOTO-STOP") {
console.log("Photo-Stop received");
photoViewOn = true;
return lastMatch;
}
I have function which return me application status
getDecision: function() {
browser.wait(protractor.ExpectedConditions.visibilityOf(decisionPage), 60000); // verify the decision and proceed further depending upon it.
element(by.xpath(rejectImage)).isPresent().then(function(result) {
if (result) {
console.log('Application is rejected');
finalResult = 'Rejected';
} else {
element(by.xpath(queueImage)).isPresent().then(function(result1) {
if (result1) {
console.log('Application is sent to underwriter for review');
finalResult = 'Queue';
} else {
console.log('Application is approved');
finalResult = 'Approved';
}
});
}
});
}
Then in same class I am returning the finalResult as :
returnDecision: function() {
console.log('Result is::' + finalResult);
return finalResult;
},
And then in admin portal again I need to take decision based upon status from above function as:
takeDecision: function(testData) {
// Verifying if the case is queued or rejected
if (decision.returnDecision() === 'Queue') {
// Approving the case
if (testData.croDecision.apprvDecision === "Approve") {
// basePage.clickElement(approveButton);
utils.sleep(2);
var approveButton = browser.driver.findElement(By.css(croApproveButton));
approveButton.click();
} else {
var declineButton = browser.driver.findElement(By.css(croDeclineButton));
declineButton.click();
utils.sleep(2);
browser.driver.findElement(By.xpath(remarkDecline)).sendKeys('for testing purpose');
}
utils.sleep(2);
browser.driver.findElement(By.linkText("Submit")).click();
utils.sleep(2);
expect(browser.driver.findElement(By.xpath(decisionSuccessMessage)).isDisplayed()).toBe(true);
} else /*if (decision.returnDecision() === 'Rejected')*/ {
console.log("Case is rejected, no further action");
}
},
and in spec file I am calling this method after I punch a case using user as follow:
fdescribe('Data Entry For Applicant(HL1)', function() {
beforeAll(function() {
this.actionwords = Object.create(require('../actionwords.js').Actionwords);
});
afterAll(function() {
testData.login.username = 'softcellqa1#gmail.com';
//logout of the app
loginPage.logout(testData)
});
fit('test', function() {
testData.loan.existingloan = 'No';
this.actionwords.housingLoanWithoutCoApplicant(testData);
this.actionwords.takeDecision(testData);
});
});
this.actionwords.takeDecision(testData) is always executed before this.actionwords.housingLoanWithoutCoApplicant
Please let me know what is wrong I am doing here?
Anyone can you help me please? I want to present the detail by following each language(english,french,khmer) from parent class. I initialize the variable as English in the constructor.I try to display the existed document follow each language but it still doesn't see.here my code!
Initialize the variable
this.events.subscribe('renderViewLang-' + this.documentId, (lang: any) => {
this.currentViewlanguage = lang;
setTimeout(() => {
this.buildContentAsHTML();
}, 1000);
});
display first loading
public ionViewDidLoad() {
console.log('ionViewDidLoad DetailTabContentPage[' + this.documentId + ']');
this.alertService.showLoading();
this.docSRV.getDocument(this.documentId, 1)
.subscribe((data: any) => {
this.docContentDTO = data;
setTimeout(() => {
if(this.currentViewlanguage === 'en'){
if(this.docContentDTO !== null) {
this.currentViewlanguage = 'en';
this.buildContentAsHTML();
}
} else if (this.currentViewlanguage === 'fr') {
if(this.docContentDTO !== null) {
this.currentViewlanguage = 'fr';
this.buildContentAsHTML();
}
} else if (this.currentViewlanguage === 'kh') {
if(this.docContentDTO != null) {
this.currentViewlanguage = 'kh';
this.buildContentAsHTML();
}
}
}, 1000);
},
error => {
this.alertService.showError(error);
},
() => { // complete
this.alertService.dismissLoading();
});
}