I am working on a Chrome Extension that works mainly within a pop-up.
I would like the user to enter some text (a string) into an input field in the pop-up, and this string will serve as a "variable" in a script I would like to inject and run on a specific page.
I have tried achieving this by making a content script that will execute the script, using the following well documented way:
var s = document.createElement('script');
s.src = chrome.runtime.getURL('pageSearch.js');
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
Basically, I would like to pass the user's input all the way to the code in pageScript.js before executing the script on the page.
What would be the best way to approach this? I will not be getting any information back to the extension.
Thanks.
To pass a variable from the popup to the dynamically inserted content script, see Pass a parameter to a content script injected using chrome.tabs.executeScript().
After getting a variable in the content script, there are plenty of ways to get the variable to the script in the page.
E.g. by setting attributes on the script tag, and accessing this <script> tag using document.currentScript. Note: document.currentScript only refers to the script tag right after inserting the tag in the document. If you want to refer to the original script tag later (e.g. within a timer or an event handler), you have to save a reference to the script tag in a local variable.
Content script:
var s = document.createElement('script');
s.dataset.variable = 'some string variable';
s.dataset.not_a_string = JSON.stringify({some: 'object'});
s.src = chrome.runtime.getURL('pageSearch.js');
s.onload = function() {
this.remove();
};
(document.head||document.documentElement).appendChild(s);
pageSearch.js:
(function() {
var variable = document.currentScript.dataset.variable;
var not_a_string = JSON.parse(document.currentScript.dataset.not_a_string);
// TODO: Use variable or not_a_string.
})();
Related
I am trying to figure out how to inject this script into a site using Tampermonkey. Here is what I have so far but the onBrazeSdkLoaded event is never being called so I don't think its being inserted correctly. I have tried a few iterations but no luck. To reference the exact script I am trying to include here is the link to the public docs.
https://www.braze.com/docs/developer_guide/platform_integration_guides/web/initial_sdk_setup/#install-gtm
const script = document.createElement("script");
var code = document.createTextNode(`+function(a,p,P,b,y){a.braze={};a.brazeQueue=[];for(var s="BrazeSdkMetadata DeviceProperties Card Card.prototype.dismissCard Card.prototype.removeAllSubscriptions Card.prototype.removeSubscription Card.prototype.subscribeToClickedEvent Card.prototype.subscribeToDismissedEvent Card.fromContentCardsJson Banner CaptionedImage ClassicCard ControlCard ContentCards ContentCards.prototype.getUnviewedCardCount Feed Feed.prototype.getUnreadCardCount ControlMessage InAppMessage InAppMessage.SlideFrom InAppMessage.ClickAction InAppMessage.DismissType InAppMessage.OpenTarget InAppMessage.ImageStyle InAppMessage.Orientation InAppMessage.TextAlignment InAppMessage.CropType InAppMessage.prototype.closeMessage InAppMessage.prototype.removeAllSubscriptions InAppMessage.prototype.removeSubscription InAppMessage.prototype.subscribeToClickedEvent InAppMessage.prototype.subscribeToDismissedEvent InAppMessage.fromJson FullScreenMessage ModalMessage HtmlMessage SlideUpMessage User User.Genders User.NotificationSubscriptionTypes User.prototype.addAlias User.prototype.addToCustomAttributeArray User.prototype.addToSubscriptionGroup User.prototype.getUserId User.prototype.incrementCustomUserAttribute User.prototype.removeFromCustomAttributeArray User.prototype.removeFromSubscriptionGroup User.prototype.setCountry User.prototype.setCustomLocationAttribute User.prototype.setCustomUserAttribute User.prototype.setDateOfBirth User.prototype.setEmail User.prototype.setEmailNotificationSubscriptionType User.prototype.setFirstName User.prototype.setGender User.prototype.setHomeCity User.prototype.setLanguage User.prototype.setLastKnownLocation User.prototype.setLastName User.prototype.setPhoneNumber User.prototype.setPushNotificationSubscriptionType InAppMessageButton InAppMessageButton.prototype.removeAllSubscriptions InAppMessageButton.prototype.removeSubscription InAppMessageButton.prototype.subscribeToClickedEvent automaticallyShowInAppMessages destroyFeed hideContentCards showContentCards showFeed showInAppMessage toggleContentCards toggleFeed changeUser destroy getDeviceId initialize isPushBlocked isPushPermissionGranted isPushSupported logCardClick logCardDismissal logCardImpressions logContentCardImpressions logContentCardsDisplayed logCustomEvent logFeedDisplayed logInAppMessageButtonClick logInAppMessageClick logInAppMessageHtmlClick logInAppMessageImpression logPurchase openSession requestPushPermission removeAllSubscriptions removeSubscription requestContentCardsRefresh requestFeedRefresh requestImmediateDataFlush enableSDK isDisabled setLogger setSdkAuthenticationSignature addSdkMetadata disableSDK subscribeToContentCardsUpdates subscribeToFeedUpdates subscribeToInAppMessage subscribeToSdkAuthenticationFailures toggleLogging unregisterPush wipeData handleBrazeAction".split(" "),i=0;i<s.length;i++){for(var m=s[i],k=a.braze,l=m.split("."),j=0;j<l.length-1;j++)k=k[l[j]];k[l[j]]=(new Function("return function "+m.replace(/\./g,"_")+"(){window.brazeQueue.push(arguments); return true}"))()}window.braze.getCachedContentCards=function(){return new window.braze.ContentCards};window.braze.getCachedFeed=function(){return new window.braze.Feed};window.braze.getUser=function(){return new window.braze.User};(y=p.createElement(P)).type='text/javascript';
y.src='https://js.appboycdn.com/web-sdk/4.2/braze.min.js';
y.async=1;(b=p.getElementsByTagName(P)[0]).parentNode.insertBefore(y,b)`);
script.text = code;
document.head.appendChild(script);
script.onload = onBrazeSdkLoaded;
A load event fires when a resource has been loaded from an external source.
Since your script is getting its program from a text node inside the script element, instead of from a URL assigned to the src attribute, there is no external source.
Consequently, there is no load event.
If you want onBrazeSdkLoaded to fire when the script you create on line 1 has loaded, then just include onBrazeSdkLoaded() in the string you assign to code.
If you want it to fire when https://js.appboycdn.com/web-sdk/4.2/braze.min.js loads, then you need to:
Ensure it is in the global scope
Write code that assigns it to y.onload inside the string you assign to code
Fix the syntax error(s) in the JS in that string
I needed to get deeper into the page so my content script is really just an injector, like this
function injectScript(file) {
var th = document.getElementsByTagName('body')[0];
var s = document.createElement('script');
s.setAttribute('type', 'text/javascript');
s.setAttribute('src', file);
th.appendChild(s);
}
injectScript( chrome.extension.getURL('/cs.js') );
Now I need to transport settings (stored in the content scripts local storage) into there as well during runtime. I can do it once no problem by creating another script node and adding the variable declaration and value in its innerHTML. Updating it this way doesn't work as well though.
Currently I have a hidden input added to the page and I write the value there and have the script on the other side periodically check and update the local one. This is just "ugh" and "eww" to me though. Is there any other way I can share the settings object between the two?
I create an iframe in a file and insert a <script> tag as its content. The Script src is loaded from a different file called test.js. Here is how it is done:
var scriptElement = document.querySelector("#your-widget");
var iframe = document.createElement("iframe");
scriptElement.parentNode.insertBefore(iframe, scriptElement.nextSibling);
var script = document.createElement("script");
iframe.contentWindow.document.appendChild(script);
script.src = "http://www.example.com/test.js";
Instead of loading the content of the script from http://www.example.com/test.js I want to take it from the same file where the above code is. This would like this:
var scriptElement = document.querySelector("#your-widget");
var iframe = document.createElement("iframe");
scriptElement.parentNode.insertBefore(iframe, scriptElement.nextSibling);
var script = document.createElement("script");
iframe.contentWindow.document.appendChild(script);
script.src = // ????
// the following JavaScript code should be placed inside the script
function mywidget() {
// some code
return true;
}
mywidget.succeeded = mywidget();
How can I set the Script Source from the same file instead of a different one?
If you literally just want to place that exact snippet in a script tag, you can just do so using .innerText.
script.innerText = 'function mywidget() { ...';
Then it will execute as is when it's inserted into the DOM. If you want to dynamically find and inject that code, read on.
There are exactly two ways to load a script on a page.
Add a <script> with the src attribute pointing to a file.
Create a <script> tag then set the contents to whatever you want to execute.
var script = document.createElement('script');
script.innerText = 'console.log("Hello, World!")';
document.body.appendChild(script);
If you want to extract part of a script and use those contents then the best you can do is load the contents via ajax and inject it using method 2.
Assuming you have jQuery (for easy AJAX work):
$.ajax({
url: 'path/to/script.js',
dataType: 'text', // make sure it doesn't get eval'd
success: function(contentsOfScript) {
// Refer to method 2
}
});
Now you can go about extracting the contents of that snippet in one of two ways:
Know exactly which line it begins on.
var lines = contentsOfScript.split('\n');
var snippet = lines.slice(lineNumber + 1); // adjust for 0 indexing
Generate a regular expression to identify where your code begins. This is rather tricky and very error prone if your snippet isn't easily distinguished from other code.
var snippet = contentsOfScript.match(/function mywidget.+/)[0];
Neither of these methods will work if you perform any minification on your code.
My javascript file with function:
scr.js:
function myf(){
alert('aaa');
}
myf();
After load page, I see dialog box with 'aaa'. This is right.
The next, I change script source to:
function myf(){
alert('bbb'); ///////////
}
myf();
and src file by add to him timestamp (for update file):
$('script[src^="./scr.js"]').attr('src','./scr.js?='+new Date().getTime());
The problems:
after update file, the myf() function doesn't run.
after run myf() function from browser console I see dialog with 'aaa' not with 'bbb'
when I remove script tag with src scr.js, I can call again my function
Where is problem and what do for update scritpt?
As far as I know, changing a script src attribute, doesn't force the browser to download the script; you need to create a new script tag and append it to the DOM.
Because the browser didn't downloaded and executed the new script.
When your script was first run by the browser it created a global function, which has been attached to the global object; that's why you can still call it, even though you've dinamically removed the script.
UPDATE (Possible solution):
Create a script element dinamically using something like this:
function createScript(src) {
var s = document.createElement("script");
s.src = src;
return s;
}
Update the DOM:
var oldScript = document.querySelector("script[src^='s.js']");
var newScript = createScript("s.js?t=" + (new Date()).getTime());
document.body.replaceChild(newScript, oldScript);
(you can translate that into jQuery if you want)
Can I get the value of a variable from another script using content script? For example from a script on Facebook. Sorry for my english.
Your content script can inject a <script> element into the page. That injected script should read the value and use DOM events to pass the value to the content script.
In your content script:
// inject a script from the extension's files
// into the execution environment of the main page
var s = document.createElement('script');
s.src = chrome.extension.getURL("value _reader.js");
document.documentElement.appendChild(s);
document.addEventListener("valueFromPageEvent", function(event) {
console.log("the content script just got the value " + event.detail.val);
});
Inside value_reader.js (which we injected in the content script above), assuming we want to the read the variable foobar from the page:
var dataObj = { "val": foobar };
var storeEvent = new CustomEvent('valueFromPageEvent', { "detail": dataObj });
document.dispatchEvent(storeEvent);
N.B.: The value_reader.js script must be in your extension's list of web_accessible_resources in the manifest.