I am developing an IE extension which works on sites opened in Internet Explorer. It is designed to work the same way as a chrome extension. I am trying to implement the Background function of chrome extension using c++ and the content script by injecting JS into the current web page. The content script, I am trying to load via IHTMLWindow2 execScript on Document load event. Now that I need to inject JS files directly I tried the following.
Had the JS file under a folder inside the Project destination and tried to inject using physical path.
std::wstring filePath(_T("d:/xx/xxx/x/x/Content/myFile.js"));
scriptText = scriptText+ filePath + endScript;
VARIANT vrt = {0};
HRESULT hrexec = ifWnd->execScript(SysAllocString(scriptText.c_str()),L"javascript", &vrt);
The scriptText has some javascript code to create script element with type and src attributes. The filePath holds the physical path towards the js file.[Also tried relative path but it was a no go]
The above was not working correctly in IE9 due to mixed content issue, upon which I researched to figure out that IE9 expects the js file to be retrieved from a server rather than local physical path. The console throws me the below exception.
SEC7111: HTTPS security is compromised by file:<filepath>
SCRIPT16388: Operation aborted
I am pretty much not sure is there any round about for injecting Javascript to the current DOM from the physical path. Please help me on this.
Also let me know is there any other possibility of injecting the JS file from the current working directory into the DOM.
You don't have to inject a <SCRIPT> tag in the DOM.
If your js file contains:
var strHello = "Hello";
function SayHello() { alert( strHello ); }
you may just read the file into memory, construct a BSTR string with it, and pass that string to IHTMLWindow2::execScript.
Later, another call to execScript with the string SayHello(); will popup the alert box. The code you injected is still here.
Related
I have a typescript project in VS code (obviously running on Node and using Debugger for Chrome extension). My html file references a static javascript file, but then that javascript file dynamically injects the contents of a javascript resource.
const body = document.querySelector('body');
const script = document.createElement('script');
script.setAttribute("rbl-script", "true");
script.innerHTML = content;
body.appendChild(script);
In the code above, content is the return data from a call using $.ajax( ajaxConfig ) with ajaxConfig.url set to 'http://localhost:8887/js/KatAppProvider.js' and that is the generated javascript file from tsc --build. (Note, I can't just statically link this file, because in production, the $.ajax request is going to a content management web service.)
If I try to set breakpoints in VS Code in the corresponding KatAppProvider.ts file, they are never hit. The problem seems to be the //# sourceMappingURL=KatAppProvider.js.map generated line. I think everything used to work fine and possibly a VS Code or extension update changed the behavior, but I can't confirm. The only way I can get this to work, is to manually modify that mapping url line in the generated file after building to //# sourceMappingURL=js/KatAppProvider.js.map (to include the js/).
Am I remembering wrong in my thinking that this worked as is (without the js/ prefix)?
Is this the expected behavior with my scenario? And if so, is there a way to change only this files compilation to include the js/ prefix (but no other files - i.e. the statically linked ones in the project)
I've built a simple html page with javascript in a separate file, called on a button press.
I've opened the html file in chrome, and the path resembles: file:///home/tom/projects/index.html
The javascript needs to read a JSON file (file:///home/tom/projects/mydata.json) which is in the same directory, using a hardcoded path.
I'm really struggling to do this. As I understand, this is because I'm using client side js (meaning I can't use the fs library for example), which is limiting my options.
According to the question here, I can't load the file if I use the URL in the format: file:///home/to.... as it gives me the error:
Cross origin requests are only supported for protocol schemes: HTTP, data, chrome, chrome-extension, https.
If I start an HTTP-server, as the answer suggests, I can use server-side modules, but if possible I would like to avoid this.
I've noticed many answers that suggest using a dialog box like this:
var selectedFile = document.getElementById('input').files[0];
function readFile (file_path) {
var reader = new FileReader();
reader.readAsText(file_path);
console.log(reader.substring(0, 100));
};
but I can't make this work with a path in the form: file:///home/tom/projects/mydata.json
Is there a way to load a .json file from a file:///home/to.... format URL using client-side javascript, with a hardcoded path (ie not asking the user to select the file from a selection box)?
This is a deliberate security restriction, to stop a user from being given, and then opening, a HTML page which then tries to read their disk.
Run your page in a webserver (as that question suggested) then you can either load the JSON from a URL (e.g. something like http://localhost/projects/mydata.json) using JavaScript, or use a server-side language to fetch it and display it inside the rendered HTML. Either way will work, the first way is probably simpler and closest to what you've got now.
It's always far better to serve HTML pages from a HTTP server, the way it's intended to be.
I found a cool feature on a website, implemented in JavaScript, I'd like to use it as is in my desktop application (for personal use).
During my experiments I managed to generate custom HTML on the fly, feed it to the browser using webBrowser1.DocumentText = [my generated HTML]
I've managed to put some inline JavaScript into the HTML, and hook it up via a ScriptManager so that I can call the JavaScript from my C# code, pass a value to it, and get a return value.
But the feature I'm trying to use is a bit more complicated: it's no less than 10 JavaScript files. 2 of them are referenced directly in the web page the usual way <script src="/js/script1.js" type="text/javascript"></script>
The other 8 are loaded in one of the scripts:
var elem = document.createElement("script");
elem.type = "text/javascript";
elem.src = "/js/" + filename;
document.body.appendChild(elem);
These 8 files are in fact data files, even though the data is represented in JavaScript. They're pretty large, over 1MB each. Stuffing it all into the HTML file seems quite stupid. Also, the script that loads the data creates a "file map" and further refers to the data based on which file it's in:
var fileMap = [
[/[\u0020-\u00ff]/, 'file1.js'],
[/[\u3000-\u30ff]/, 'file2.js'],
[/[\u4e00-\u5dff]/, 'file3.js'],
...
I don't want to resort to modifying the JavaScript, because it's not exactly my strong point. So the browser needs to "see" the js files in order to be able to use them. I thought of creating the file structure locally, and navigating the browser there. But I don't want any loose files in my solution. I'd like to have everything embedded if possible. And I doubt I can get the browser to navigate to an embedded resource, and see other embedded resources as files. Any idea how I could get around this?
EDIT:
I've tried to do it with local files. No luck. I get the HTML to load properly, but when I try to invoke a JavaScript call, nothing happens. I tried pointing the browser to those js files, to make sure they're there. They are. I tried an element with src attribute pointing to an image in the same subfolder as the script files. It gets rendered. It's as if external js files refuse to load.
I had a similar need as your scenario and I addressed it using two key points embedded in two other Stack Overflow answers. As noted by SLaks' answer here the first key is using the syntax file:/// as the prefix for an absolute path to external files. The second is using .Replace("\\", "/") for an absolute file path as listed in Adam Plocher's answer and one of his follow-up comments here.
In short, the final output for each external file in an HTML page will look something like:
<link href="file:///c:/users/david/myApp/styles/site.css" rel="stylesheet" type="text/css">
or
<script src="file:///c:/users/david/myApp/scripts/JavaScript1.js"></script>
Using the format in the samples above in my HTML file resulted in the WebBrowser control loading external CSS, image or script files.
The details and solving the scenario in the question
In the womd's answer in the first referenced SO answer above he used the method System.IO.File.ReadAllText() to load script files and embedded the text of the script files into the <head> tag. As you indicated in your question loading script files directly into the HTML page is not what you're looking to do.
The solution below involves using the same System.IO.File.ReadAllText() method but loads the text of the HTML page instead. The premise works similar to the Razor View Engine in ASP.NET.
The main idea in the solution below involves adding a temporary string in an HTML page that will be loaded into the WebBrowser control and then replacing this temporary string in a C# method in my app just before the HTML page is set to be loaded into the WebBrowser control.
Here are the basic steps to my solution:
Add a temporary string for each external reference in the HTML file.
Declare a variable for the absolute path in a script tag within the HTML file. This step is not necessary unless you're going to use the absolute path elsewhere within your JavaScript code. Your scenario involves delay loading external script files via JavaScript code so this step was necessary.
Modify the src property in the JavaScript code that delay loads the other script files with the absolute path variable.
Add a method in your app to loads the HTML page file as a text string and then replaces all temporary string instances with an absolute path containing the prefix 'file:///'. The absolute path should have forward slashes.
Set the 'DocumentText' property on the WebBrowser control to the updated HTML.
Set the 'Copy to Output Directory' of each external file in your project to 'Copy always' or 'Copy if newer'. This step may not be necessary if you have a fixed location to your external files and that location is not within the build or publish directory used by Visual Studio.
The following are the details for each step. I added a lot of detail that you can skip. I was verbose to reduce any confusion since the steps make changes to several places in the project.
1. Using a temporary string
I used the string "/ReplaceWithAbsolutePath/" but you can use any distinct text. Each reference to an external file in the HTML page looks like:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<link href="/ReplaceWithAbsolutePath/styles/site.css" rel="stylesheet" type="text/css">
<script type="text/javascript">
var absolutePath = "/ReplaceWithAbsolutePath/";
</script>
</head>
<body>
<p>My web page</p>
<script src="/ReplaceWithAbsolutePath/scripts/JavaScript1.js"></script>
</body>
</html>
2. Declare absolute path variable
Note in the above HTML page I listed a <script> tag with the declared variable 'absolutePath' set to the temporary string. (In the HTML page above the variable is added a global variable and that is not necessarily best practice. You can declare the variable within a namespace instead of declaring it in the global namespace.)
3. Modify the delay load script to include absolute path variable
Add the 'absolutePath' variable to your JavaScript file that delay loads other JavaScript files containing your data.
elem.src = absolutePath + "/js/" + filename;
4. C# method to replace all temporary string instances
Within your project add the following line to your form load event handler or place this line somewhere in your initialization of the WebBrowser control.
webBrowser1.DocumentText = GetUpdatedHtmlWithAbsolutePaths("/ReplaceWithAbsolutePath/", "HTMLPage1.html");
Add the following method to your code. Update the call to the method in the line above with the name of the class instance where the following method is placed.
// The result of this method will look like the following example:
// <script src="file:///c:/users/david/documents/myApp/scripts/JavaScript1.js"></script>
public string GetUpdatedHtmlWithAbsolutePaths(string tempPathString, string htmlFilename)
{
// Get the directory as the application
// stackoverflow.com/questions/674857/should-i-use-appdomain-currentdomain-basedirectory-or-system-environment-current
// Note that the 'BaseDirectory' property will return a string with trailing backslashes ('\\')
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
// Replace '//' with '/' in the appDirectory string
appDirectory = appDirectory.Replace("\\", "/");
// Read all of the HTML text from the HTML page file
string html = System.IO.File.ReadAllText(appDirectory + #"\" + htmlFilename);
// Replace all '/ReplaceWithAbsolutePath/' strings within the HTML text with
// the absolute path on the local machine
html = html.Replace(tempPathString, "file:///" + appDirectory);
return html;
}
5. Set the DocumentText property of the WebBrowser control
I added the initialization of the WebBrowser control in the form load event handler but you can, of course, add the line that sets the DocumentText property wherever you initialize your WebBrowser control.
private void Form1_Load(object sender, EventArgs e)
{
// Set the document text of the web browser control with the updated HTML
webBrowser1.DocumentText = GetUpdatedHtmlWithAbsolutePaths("HTMLPage1.html");
}
6. Set the 'Copy to Output Directory' of each external file
Take a look at the answer posted by Matthew Watson in this Stack Overflow question if you want your external files included in your solution/project file structure.
You can add files to your project and select their properties: "Build
Action" as "Content" and "Copy to output directory" as "Copy Always"
or Copy if Newer (the latter is preferable because otherwise the
project rebuilds fully every time you build it).
Then those files will be copied to your output folder.
This is better than using a post build step because Visual Studio will
know that the files are part of the project. (That affects things like
ClickOnce applications which need to know what files to add to the
clickonce data.)
In short, add the external file to your project. You can add the external to any subfolder in your project. (In Visual Studio 2013 or 2015 -- I don't have VS2012) Right-click on the external file in the Solution Explorer and select Properties from the context menu. The Properties pane will be displayed. In the Properties pane change the setting for 'Copy to Output Directory' to 'Copy always' or 'Copy if newer'.
Use View Source to verify absolute path strings
Run your project and it should load your external files in the WebBrowser control. Assuming you have not set the property wbChartContainer.IsWebBrowserContextMenuEnabled = false; in code or in the Properties pane for WebBrowser control you can right-click on the WebBrowser control when your form is running. Click 'View Source' from the context menu and check the paths to your external resources in the View Source window.
I'm trying to load some external javascript code in my Chrome extension. However, due to the sandboxed environment of the extension, I can't see any of the functions defined by the external code.
The external code implements a dependency mechanism, whereby one javascript file may require another, and so forth. It also looks at arguments to the URL used to load the javascript to determine the top level javascript file to load. So it basically is able to load any arbitrary web app, and it is not known in advance all the files that will be used. So I can't use any static definitions in the extension.
There is also the issue that since all extension code is sandboxed, I don't have complete access to the document - for instance, it can't access the window variable.
But if I put all the code in the external code, I run into content security problems if one script tries to load another. The whole reason I went was an extension is because of the bone-headed misimplementation of CSP by every single browser in existence whereby bookmarklets can't access external resources.
What's the best practice for bypassing or working around the extension sandbox to basically be able to run code as if the page itself had loaded it, without any issues with CSP?
In the content script you could do something like this to load the js file
function inject(url, exteral) {
// 1. Build the absolute URL
// 2. Create a script tag and set src attribute
// 3. Append script tag to thw window
if (!external){
url = chrome.extension.getURL(url);
}
var scriptElement = document.createElement('script');
scriptElement.src = url;
(document.body || document.head || document.documentElement).appendChild(scriptElement);
}
If the js file is packged with the extension, it must be placed in the
manifest.json under web_accesible_resources.
othwerwise it need to have the same protocol that the page it is injected in (http | https)
Since the content script is not allowed to call the window functions, you could
call window.postMessage to send data from the actual window to content script.
What are the possible cross-browser (at least Firefox & Chrome) methods to dynamically reload a local JavaScript file that is referenced by a locally loaded HTML file?
Background:
A local HTML page is being used to render some data that is formatted and displayed by two referenced JavaScript files. One file contains the JavaScript code and the other file contains JSON data.
This JSON data is updated on disk by another program and it would be nice to have the UI automatically incorporate these updates without manually reloading the page (or opening a new page).
In Firefox, I believe the issue could be resolved using AJAX to load the HTML, but in Chrome this will not work due to the same origin policy failures (I unfortunately cannot necessarily rely on --disable-web-security to mitigate this since all prior instances of Chrome must be closed for that to work).
The only solution I see is to run a local web server, but I am hoping for something simpler and less invasive (Perhaps loading the JavaScript in an iframe and reloading the iframe, although I imagine this would be prevented by browser security).
Does anyone have any recommendations?
If your app starts up Chrome then you can include the --allow-file-access-from-files flag in the start command.
I use the following code to reload JavaScript and JSON files.
/* Load a JavaScript or JSON file to use its data */
function loadJsFile(filename, dataIsLoaded){
var fileref = document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filename);
fileref.onload = dataIsLoaded;
if (typeof fileref!="undefined"){
document.getElementsByTagName("head")[0].appendChild(fileref);
}
}
/* The callback that is invoked when the file is loaded */
function dataIsLoaded(){
console.log("Your data is ready to use");
}
Usage when the JSON file is in the same directory as the website:
var jsonFile= "myData.json";
loadJsFile(jsonFile, dataIsLoaded);
I tested it successfully in IE10 and Firefox 22; it doesn't work in Chrome though.