This npm library gives your code access to a internet browser(in app browser) of its own, an alternative to WebView and this as you can imagine has a limitless amount of use cases, easiest to think of would be testing of websites another one which is the reason this library was initially developed was to map UI based navigation routes through websites to compact them down for access outside of browsers themselves but apart from those two use cases their may be plenty more just waiting to be discovered.
How exactly does it work though ? well how it works is it alters certain restrictions on these nested browsing contexts (iframe in particular.) related to navigation of the internet and then sets up a connection from parent browsing context to child nested browsing contexts (iframe/webview) for communication, before the connection can be established though first the requests & their responses of interest to these nested browsing contexts must be identified, handled and modifications must be made to the response, always keeping in mind why the restrictions between these two contexts and on the child context itself were initially put in place and testing to ensure that the alterations made do not negate the security these restrictions provide, this process will be re-run from page to page as you navigate the current website that you have chosen, also just quick side note the reason for the particular open source license that was applied to this library is again due to ensuring security of the data that these browsing contexts may provide at any given time.
Lastly although this library is compatible with the use of webview element as your nested browsing context it was not designed to be when written, it was designed for use with iframes and as such a lot of the headers that restrict iframes from navigating and displaying internet content freely will not affect webviews and with that being said if you do choose to use webviews instead with this library don't be alarmed if you discover you can navigate across different origins this is simply just something webviews can do unlike iframes and keep in mind when you use iframes you will be restricted from directly navigating across different origins from the nested browsing context for security reasons.
- Supported Frameworks
- Getting Started
- Advanced Options
- Library Classes
- Client
- General
- Native
- AddFileSchemaToFrameAnscestorsChainLink
- BodyScriptInjectionChainLink
- RemoveXFrameOptionsChainLink
- ResponseModificationChainLink
- ResponsePreparationChainLink
- WebsitePageFrameNavigationResponsePreparation
- WebsitePageFrameResourceResponsePreparation
- ElectronCacheFactory
- Interceptor
- NetworkRequestInterceptor
- ElectronNetworkRequestInterceptor
- Cache
- HeaderMap
- NetworkMessage
- Request
- Response
- NetworkRequestHandler
- ElectronNetworkRequestHandler
- Protocol
- Script
- WebsitePageFrameCrossOriginMessagingScript
- WebsitePageFrame
- ElectronWebsitePageFrame
- Open Source License
- Electron Version 6.
- Electron Version 7, will be looking into support for this revision, issue is "provisional headers" are only provided to interceptor among other issues that are keeping this library from support for this revision of electron.
The list although quiet short at the moment could be extended out into others with some work if needs be either officially or by others that choose to build on top of the work that has been done here, the reason as to why more where not added to the list is that this release really focused around ensuring that this library was efficient, extensible and secure.
This section makes the assumption that you already have a Electron application setup and are ready to start adding this library to its package dependency list.
First thing you want to do is add the library to your packages dependency list doing it like so.
npm install website-page-frame
once you have done this you may move onto the next step.
The below code although simple the function name must be typed exactly as is shown, as it is used by the libraries client side class WebsitePageFrameConnection currently as communication method between electrons renderer process and main to invoke a function which will clear the cache of all its current information.
// yourPreloadScriptWithIpcRendererCacheClearCall.js
var ipcRenderer = require("electron").ipcRenderer;
window.ipcRendererClearCache = function() {
ipcRenderer.sendSync("clear-cache");
};
If you are interested as to why a cache was needed read the below answer to this question but if not simply move to the next step in the setup process.
So why is a cache needed ? well if you read the Walk through thoroughly you would have noticed it states that the process of identification & modification of the response is re-run from page to page as you navigate the current website and what is meant by that is that when you initially open a website in the nested browsing context from the URL the origin of interest is extracted and stored in the cache to ensure that every subsequent request made by this nested browsing context can be verified ensuring that it only has access to what it is supposed to have access to, for instance lets say we opened in our nested browsing context "https://malicioussite.com#websitePageFrame" and it in turn attempted to open its own child nested browsing context to lets say Facebook or Twitter in order to take advantage of the restrictions this library removes well without the Current Nested Browsing Context Origin how could we in fact tell that these restrictions should not be removed and that's just it as of planning this release their was no safe way found of allowing navigation in the nested browsing context without this, that's not to say that their isn't another way just that another was not found.
NOTE: Before you get started with this step read on past Option 1 (Options are given here as their may be support for HTTP for this revision in the future.) first to Basic Resulting main.js file to get an idea of where the code should be placed in your Electron main.js file.
// main.js(or equivalent) file.
var ElectronWebsitePageFrame = require("website-page-frame").ElectronWebsitePageFrame;
...
var electronWebsitePageFrame = new ElectronWebsitePageFrame();
electronWebsitePageFrame.setupProtocolInterceptors();
After you have decided and went with one of the following setups your code in Electron main.js file should look something like what is below, although here Option 1 is used.
// main.js(or equivalent) file.
var ElectronWebsitePageFrame = require("website-page-frame").ElectronWebsitePageFrame;
const { app, BrowserWindow } = require("electron");
let appWindow;
function createWindow() {
appWindow = new BrowserWindow({ width: 800,
height: 600,
webPreferences: {
// LINUX OR MAC
preload: `${__dirname}/yourPreloadScriptWithIpcRendererCacheClearCall.js`
// WINDOWS
preload: `${__dirname}\\yourPreloadScriptWithIpcRendererCacheClearCall.js`
}
});
// LINUX OR MAC
appWindow.loadFile(`${__dirname}/index.html`);
// WINDOWS
appWindow.loadFile(`${__dirname}\\index.html`);
var electronWebsitePageFrame = new ElectronWebsitePageFrame();
electronWebsitePageFrame.setupProtocolInterceptors();
appWindow.on("closed", () => {
appWindow = null;
});
}
app.on("ready", createWindow);
Once you have something resembling this code in your main.js file move onto the next step.
This step is here to show you how the client side libraries classes should be imported into your apps renderer process in order to make use of them in the Electron GUI.
<!-- index.html(or equivalent) HTML file -->
<!DOCTYPE html>
<html>
<head>
<title> My App Index Page </title>
</head>
<body>
<h1> My First App </h1>
<iframe src="" id="yourNestedBrowsingContext" width="1000px" height="1000px" sanbox="allow-forms allows-scripts allows-same-origin"></iframe>
<!--- LINUX OR MAC --->
<script src="./node_modules/website-page-frame/dist/client-bundle.js"></script>
<!-- WINDOWS --->
<script src=".\node_modules\website-page-frame\dist\client-bundle.js"></script>
</body>
</html>
NOTE: here I have applied sanbox restrictions on my iframe and it might seem dangerous that I have applied allow-same-origin and allow-scripts at the same time, but just know it is only dangerous if the parent pages origin where the same as the nested pages origin which in our case it wont because we load our content locally our parent page origin should be file:// which will not match our nested browsing contexts as it will either always be https://www.whatever-website.com, for more information on how to use setup your iframe securely or just more information about iframes in general visit https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element.
Once you have added this you are pretty much done with the setup and can move onto the last step to test out the in app internet browsing capabilities of this library for yourself.
So this step will show you some of the basics to making use of this library, you should work your way down through each sub step.
<!-- index.html(or equivalent) HTML file -->
<!DOCTYPE html>
<html>
<head>
<title> My App Index Page </title>
</head>
<body>
<h1> My First App </h1>
<iframe src="" id="yourNestedBrowsingContext" width="1000px" height="1000px" sanbox="allow-forms allows-scripts allows-same-origin"></iframe>
<!---LINUX OR MAC--->
<script src="./node_modules/website-page-frame/dist/client-bundle.js"></script>
<!-- WINDOWS --->
<script src=".\node_modules\website-page-frame\dist\client-bundle.js"></script>
<script>
var childNestedBrowsingContext = document.getElementById("yourNestedBrowsingContext").contentWindow;
var childNestedBrowsingContextInitialURL = "https://www.youtube.com";
var maxConnectionEstablishmentRetries = 10;
var websitePageFrameConnection = new websitePageFrame.WebsitePageFrameConnection(childNestedBrowsingContextInitialURL,
childNestedBrowsingContext,
maxConnectionEstablishmentRetries);
var clientsParentToChildMessageResponseTimeoutMilliseconds = 10000;
var websitePageFrameClient = new websitePageFrame.WebsitePageFrameClient(websitePageFrameConnection,
clientsParentToChildMessageResponseTimeoutMilliseconds);
websitePageFrameConnection.openNewConnection();
</script>
</body>
</html>
What has been added here is a script element below our library importing script element that we added earlier that demonstrates how to setup your in app browser making use of some of the features from client side classes WebsitePageFrameClient and WebsitePageFrameConnection, note the childNestedBrowsingContextInitialURL can be changed to whatever you wish, in this instance what will be displayed when the app runs and opens this website in the nested browsing context can be seen below.
Navigation between different pages on the same website the in app browser can do without any problems, keeping communication between parent and child open as well as page display restrictions removed but keep in mind that navigation away from the current website to another invoked by child nested browsing context will more than likely fail as it has not been design as of yet for smooth navigation from within the nested browsing context between websites.
Well how do we navigate our child nested browsing context to different websites then ? well how that is done is demonstrated in the next sub step of testing out this library so go on give it a read and try it out.
<!-- index.html(or equivalent) HTML file -->
<!DOCTYPE html>
<html>
<head>
<title> My App Index Page </title>
</head>
<body>
<h1> My First App </h1>
<iframe src="" id="yourNestedBrowsingContext" width="1000px" height="1000px" sanbox="allow-forms allows-scripts allows-same-origin"></iframe>
<!--- LINUX OR MAC --->
<script src="./node_modules/website-page-frame/dist/client-bundle.js"></script>
<!-- WINDOWS --->
<script src=".\node_modules\website-page-frame\dist\client-bundle.js"></script>
<script>
var childNestedBrowsingContext = document.getElementById("yourNestedBrowsingContext").contentWindow;
var firstChildNestedBrowsingContextInitialURL = "https://www.google.com";
var maxConnectionEstablishmentRetries = 10;
var websitePageFrameConnection = new websitePageFrame.WebsitePageFrameConnection( firstChildNestedBrowsingContextInitialURL,
childNestedBrowsingContext,
maxConnectionEstablishmentRetries);
var clientsParentToChildMessageResponseTimeoutMilliseconds = 10000;
var websitePageFrameClient = new websitePageFrame.WebsitePageFrameClient( websitePageFrameConnection,
clientsParentToChildMessageResponseTimeoutMilliseconds);
websitePageFrameConnection.openNewConnection();
setTimeout(function() {
var secondWebsitesUrlIWishToNavigateChildBrowsingContextTo = "https://www.twitter.com";
websitePageFrameConnection.updateChildBrowsingContextCurrentLocationInformation( secondWebsitesUrlIWishToNavigateChildBrowsingContextTo );
websitePageFrameConnection.openNewConnection();
}, 4000);
</script>
</body>
</html>
The three new lines of code we added are all that is needed in order to navigate your in app browser from one website to the next safely without, all we simple do here is take advantage of more features that the class WebsitePageFrameConnection has to offer.
The previous steps have had a look into how to get things setup aswell and opening your first website in your in app browser now this sub step adds additional code which demonstrates how you can interact as the title suggests with the in app browsers page in the code you write.
<!-- index.html(or equivalent) HTML file -->
<!DOCTYPE html>
<html>
<head>
<title> My App Index Page </title>
</head>
<body>
<h1> My First App </h1>
<iframe src="" id="yourNestedBrowsingContext" width="1000px" height="1000px" sanbox="allow-forms allows-scripts allows-same-origin"></iframe>
<!--- LINUX OR MAC --->
<script src="./node_modules/website-page-frame/dist/client-bundle.js"></script>
<!-- WINDOWS --->
<script src=".\node_modules\website-page-frame\dist\client-bundle.js"></script>
<script>
var childNestedBrowsingContext = document.getElementById("yourNestedBrowsingContext").contentWindow;
var firstChildNestedBrowsingContextInitialURL = "https://www.google.com";
var maxConnectionEstablishmentRetries = 10;
var websitePageFrameConnection = new websitePageFrame.WebsitePageFrameConnection( firstChildNestedBrowsingContextInitialURL,
childNestedBrowsingContext,
maxConnectionEstablishmentRetries);
var clientsParentToChildMessageResponseTimeoutMilliseconds = 10000;
var websitePageFrameClient = new websitePageFrame.WebsitePageFrameClient( websitePageFrameConnection,
clientsParentToChildMessageResponseTimeoutMilliseconds);
var messageBuilder = new websitePageFrame.WebsitePageFrameMessageBuilder();
var actionParameter = "actionBeingCarriedOut";
var actionArguement = "getPageTitle";
var getPageTitle = new websitePageFrame.Action([actionParameter], [actionArguement], function() {
console.log("The name of the function that has been invoked is: " + actionBeingCarriedOut);
postMessageDataReturned.childNestedBrowsingContextsPageTitle = document.getElementsByTagName("title")[0].innerText;
});
messageBuilder.attachAction(getPageTitle);
websitePageFrameConnection.openNewConnection();
setTimeout(function() {
websitePageFrameClient.sendMessage( messageBuilder.finishBuild() );
}, 2000);
window.waitForResponseBrowserThread = new websitePageFrame.BrowserThread(function() {
if (websitePageFrameClient.getResponse() !== null) {
alert(websitePageFrameClient.getResponse().childNestedBrowsingContextsPageTitle);
waitForResponseBrowserThread.stop();
}
}, 1000);
waitForResponseBrowserThread.start();
</script>
</body>
</html>
The code we add here introduces the use of three new client side classes BrowserThread, MessageBuilder and Action in order to create a message in the parent browsing context, send it off to the child browsing context and log some information to the console from the child browsing context making use of argument passed along with the action to be invoked and wait until response is returned to retrieve and display the title of the child browsing context page that was added to the response object(which is postMessageDataReturned.) and sent to the parent browsing context.
These are the basics of what you can do with this library for more go have a read through Advanced Options and if you want a better understanding of the classes used here and more go have a read through Library Classes which mostly organizes the classes out into categories of where they are intended to be used with the exception of General which holds classes that could be used in either environment or be extended through sub classes for use in either environment.
The advanced options section is for those who wish to dig a little deeper into how exactly the in app browser works behind the scenes as well those who simply wish to add to or alter the existent behavior of this in app browser.
This subsection here is going to walk through step by step how to add additional response modification tasks to be run each time a new in app browser navigation or resource(resource requests are those for assets such as styles sheets, images that are to be shown or style the HTML page that has already been received.) request is triggered and response for that requests has been received on the backend(outside of renderer process.).
So first thing we are going to do is create our class that will extend ResponseModificationChainLink it is recommended to read up on this class by clicking the link before moving on to better understand how it is used, once this classes use is understood the code for the class that we are going to write should look something like what is below differing in size based on the amount of work that task will undertake, but generally each task should have 1 job as you can simply write more and group them together to do bigger jobs.
// MyCustomResponseModificationChainLink.js
var ResponseModificationChainLink = require("website-page-frame").native.ResponseModificationChainLink
function MyCustomResponseModificationChainLink() {
ResponseModificationChainLink.call(this);
this._customHeaderToAddToResponse = "header_to_add";
this._customHeaderValueToAdd = "header_value";
this._customHeaderToRemoveFromResponse = "header_to_remove";
}
MyCustomResponseModificationChainLink.prototype = Object.create(MyCustomResponseModificationChainLink.prototype);
MyCustomResponseModificationChainLink.prototype.constructor = MyCustomResponseModificationChainLink;
MyCustomResponseModificationChainLink.prototype.preformTask = function(response) {
response.getHeaderMap().set(this._customHeaderToAddToResponse, this._customHeaderValueToAdd);
response.remove(this._customHeaderToRemoveFromResponse);
};
exports.MyCustomResponseModificationChainLink = MyCustomResponseModificationChainLink;
The above is somewhat what the classes code should look like (ES5 class syntax is used here.) although again differing is naming & size based on the job that it is that your task should be carrying out, after you have completed this you may move onto the the next step.
2. Adding newly created MyCustomResponseModificationChainLink to in app navigation and resource response modification chains
So for this step we are going to go back to our main.js file or equivalent electron app entry script that should look something like this based on what option you chose in the Getting Started section.
// main.js(or equivalent) file.
var ElectronWebsitePageFrame = require("website-page-frame").native.ElectronWebsitePageFrame;
const { app, BrowserWindow } = require("electron");
let appWindow;
function createWindow() {
appWindow = new BrowserWindow({ width: 800,
height: 600,
webPreferences: {
// LINUX OR MAC
preload: `${__dirname}/yourPreloadScriptWithIpcRendererCacheClearCall.js`
// WINDOWS
preload: `${__dirname}\\yourPreloadScriptWithIpcRendererCacheClearCall.js`
}
});
// LINUX OR MAC
appWindow.loadFile(`${__dirname}/index.html`);
// WINDOWS
appWindow.loadFile(`${__dirname}\\index.html`);
var electronWebsitePageFrame = new ElectronWebsitePageFrame();
electronWebsitePageFrame.setupProtocolInterceptors();
appWindow.on("closed", () => {
appWindow = null;
});
}
app.on("ready", createWindow);
And what we are going to do is import the necessary files to add on our newly created ResponseModificationChainLink class descendant to the normal flow of the in app browser like so.
// main.js(or equivalent) file.
var ElectronWebsitePageFrame = require("website-page-frame").native.ElectronWebsitePageFrame;
var ElectronCacheFactory = require("website-page-frame").native.ElectronCacheFactory;
var WebsitePageFrameNavigationResponsePreparation = require("website-page-frame").native.WebsitePageFrameNavigationResponsePreparation;
var WebsitePageFrameResourceResponsePreparation = require("website-page-frame").native.WebsitePageFrameResourceResponsePreparation;
var ElectronNetworkRequestHandler = require("website-page-frame").native.ElectronNetworkRequestHandler;
var ElectronNetworkRequestInterceptor = require("website-page-frame").native.ElectronNetworkRequestInterceptor;
var ResponsibilityChainBuilder = require("website-page-frame").general.ResponsibilityChainBuilder;
var Protocol = require("website-page-frame").native.Protocol;
var MyCustomResponseModificationChainLink = require("../wherever/class/is/directory/wise/MyCustomResponseModificationChainLink.js").MyCustomResponseModificationChainLink;
const { app, BrowserWindow } = require("electron");
let appWindow;
function createWindow() {
appWindow = new BrowserWindow({ width: 800,
height: 600,
webPreferences: {
// LINUX OR MAC
preload: `${__dirname}/yourPreloadScriptWithIpcRendererCacheClearCall.js`
// WINDOWS
preload: `${__dirname}\\yourPreloadScriptWithIpcRendererCacheClearCall.js`
}
});
// LINUX OR MAC
appWindow.loadFile(`${__dirname}/index.html`);
// WINDOWS
appWindow.loadFile(`${__dirname}\\index.html`);
var cache = (new ElectronCacheFactory()).create();
var responsePreparationChainBuilder = new ResponsibilityChainBuilder();
var websitePageFrameNavigationResponsePreparation = new WebsitePageFrameNavigationResponsePreparation();
var websitePageFrameResourceResponsePreparation = new WebsitePageFrameResourceResponsePreparation();
websitePageFrameNavigationResponsePreparation.getTaskChain().push(new MyCustomResponseModificationChainLink());
websitePageFrameResourceResponsePreparation.getTaskChain().push(new MyCustomResponseModificationChainLink());
responsePreparationChainBuilder.attachChainLink( websitePageFrameNavigationResponsePreparation );
responsePreparationChainBuilder.attachChainLink( websitePageFrameResourceResponsePreparation );
var electronNetworkRequestHandler = new ElectronNetworkRequestHandler( session.defaultSession,
cache,
responsePreparationChainBuilder );
var electronHttpsRequestInterceptor = new ElectronNetworkRequestInterceptor( new Protocol("https"),
electronNetworkRequestHandler );
interceptors = [ electronHttpsRequestInterceptor ];
var electronWebsitePageFrame = new ElectronWebsitePageFrame(interceptors);
electronWebsitePageFrame.setupProtocolInterceptors();
appWindow.on("closed", () => {
appWindow = null;
});
}
app.on("ready", createWindow);
This is again is somewhat what your code should now look like now in your main.js file, so now your all done from here the task that is to be preformed along side those already added to the task chain will be executed with each in app browser navigation or resource request, just a side note though what should generally be used and needs to be used to set the taskChain variable for any other ResponsibilityChainLink is the TaskChainBuilder it helps to keep the setup process neat and tidy for these classes but why is it not used here then ? well that is because these classes ( WebsitePageFrameNavigationResponsePreparation and WebsitePageFrameResourceResponsePreparation ) already have there own preset filled taskChain variable and I do not want to mistakenly disrupt that as it these preset tasks are essential to the in app browser so here I modify taskChain variable directly so as not to unintentionally add or remove any of the preset tasks.
Before we get started here it is probably best if you go take a look at ResponsePreparationChainLink if you have not already to get a better understanding of its use.
So what we are going to do now is run through the steps to create your own ResponsePreparationChainLink sub class to be used for whatever purpose you deem fit in our case all we are going to do is create one that will count all the incoming data that is not related to our in app browser both HTTPS, now one thing to keep note of is the NetworkRequestHandlers responsePreparationChain is searched through for a ResponsePreparationChainLink or descendant of that, from the start of the Array to the end so if you put this or any other ResponsePreparationChainLink descendant at the very start of the Array their is a high chance that it will interfere with the in app browsers key components WebsitePageFrameNavigationResponsePreparation & WebsitePageFrameResourceResponsePreparation if they are not placed at the start of the array as the responsePreparationChain is only searched to find the ResponsePreparationChainLink or descending classes incoming traffic that they are responsible for, once found they will run through some tasks and any ResponsePreparationChainLink or descending classes after the responsible one found in the responsePreparationChain will simply be skipped.
If you have followed and understand all of the above you should be ready to move on to the first step.
So as I mentioned the class we are extending is ResponsePreparationChainLink and what your subclass should look like is something like what is shown below differing in length based on what you choose for its use to be.
var TaskChainBuilder = require("website-page-frame").native.TaskChainBuilder;
var ResponsePreparationChainLink = require("website-page-frame").native.ResponsePreparationChainLink;
var IncrementCacheTrafficCounter = require("../where/directory/wise/is/IncrementCacheTrafficCounter.js").IncrementCacheTrafficCounter;
var taskChainBuilder = new TaskChainBuilder();
taskChainBuilder.attachChainLink(new IncrementCacheTrafficCounter());
function IncomingTrafficCounter() {
ResponsePreparationChainLink.call(this, taskChainBuilder);
}
IncomingTrafficCounter.prototype = Object.create(ResponsePreparationChainLink.prototype);
IncomingTrafficCounter.prototype.constructor = IncomingTrafficCounter;
IncomingTrafficCounter.prototype.checkIfResponsible(request, response, cache) {
return true;
};
exports.IncomingTrafficCounter = IncomingTrafficCounter;
Although this is simple enough to write their are a few things to keep note of IncrementCacheTrafficCounter ResponseModificationChainLink is yet to be written by us and we will do this in the next step, also their is one method we simple override as a catch all of the responsePreparationChain of ours by ensuring that it always returns true each time the HTTPS traffic comes in that the in app browser is not responsible for this Responsibility Chain Link will be run executing all of its taskChain tasks which in our case is just the one which will increment the caches traffic counter each time this ResponsePreparationChainLink is found to be responsible for incoming traffic, now move on to the next step.
Here what we are going to do is create a subclass of ResponseModificationChainLink that will on first time running create a key-value pair in the cache for key "IncomingTrafficCounter" and set it to 1 and each subsequent time this classes preformTask method is run it will increment the key "IncomingTrafficCounter" value by 1 each time, code for this class can be seen below.
var ResponseModificationChainLink = require("website-page-frame").native.ResponseModificationChainLink;
function IncrementCacheTrafficCounter() {
ResponseModificationChainLink.call(this)
}
IncrementCacheTrafficCounter.prototype = Object.create(ResponseModificationChainLink.prototype);
IncrementCacheTrafficCounter.prototype.constructor = IncrementCacheTrafficCounter;
IncrementCacheTrafficCounter.prototype.preformTask = function(response, cache) {
if ( cache.has("IncomingTrafficCounter") ) {
cache.set("IncomingTrafficCounter", cache.get("IncomingTrafficCounter") + 1 );
} else {
cache.set("IncomingTrafficCounter", 1);
}
console.log( "traffic counter is now: " + cache.get("IncomingTrafficCounter"));
};
exports.IncrementCacheTrafficCounter = IncrementCacheTrafficCounter;
Now that we have create IncomingTrafficCounter ResponsePreparationChainLink descendant and IncrementCacheTrafficCounter ResponseModificationChainLink descendant it is now time we move onto the next and final step of adding them the setup of main.js file or equivelant from the Getting Started section.
The below code displays somewhat what your setup should look like in your electron main.js file or equivelant, again one thing to keep note of is the order of the ResponsePreparationChainLinks sub classes with the key in app browser classes coming first ensuring that **IncomingTrafficCounter is added to the end of the responsePreparationChain Array.
// main.js(or equivalent) file.
var ElectronWebsitePageFrame = require("website-page-frame").native.ElectronWebsitePageFrame;
var ElectronCacheFactory = require("website-page-frame").native.ElectronCacheFactory;
var WebsitePageFrameNavigationResponsePreparation = require("website-page-frame").native.WebsitePageFrameNavigationResponsePreparation;
var WebsitePageFrameResourceResponsePreparation = require("website-page-frame").native.WebsitePageFrameResourceResponsePreparation;
var ElectronNetworkRequestHandler = require("website-page-frame").native.ElectronNetworkRequestHandler;
var ElectronNetworkRequestInterceptor = require("website-page-frame").native.ElectronNetworkRequestInterceptor;
var ResponsibilityChainBuilder = require("website-page-frame").general.ResponsibilityChainBuilder;
var Protocol = require("website-page-frame").general.Protocol;
var IncomingTrafficCounter = require("../wherever/class/is/directory/wise/IncomingTrafficCounter.js").IncomingTrafficCounter;
const { app, BrowserWindow } = require("electron");
let appWindow;
function createWindow() {
appWindow = new BrowserWindow({ width: 800,
height: 600,
webPreferences: {
// LINUX OR MAC
preload: `${__dirname}/yourPreloadScriptWithIpcRendererCacheClearCall.js`
// WINDOWS
preload: `${__dirname}\\yourPreloadScriptWithIpcRendererCacheClearCall.js`
}
});
// LINUX OR MAC
appWindow.loadFile(`${__dirname}/index.html`);
// WINDOWS
appWindow.loadFile(`${__dirname}\\index.html`);
var cache = (new ElectronCacheFactory()).create();
var responsePreparationChainBuilder = new ResponsibilityChainBuilder();
responsePreparationChainBuilder.attachChainLink( new WebsitePageFrameNavigationResponsePreparation() );
responsePreparationChainBuilder.attachChainLink( new WebsitePageFrameResourceResponsePreparation() );
responsePreparationChainBuilder.attachChainLink( new IncomingTrafficCounter() );
var electronNetworkRequestHandler = new ElectronNetworkRequestHandler( session.defaultSession,
cache,
responsePreparationChainBuilder );
var electronHttpsRequestInterceptor = new ElectronNetworkRequestInterceptor( new Protocol("https"),
electronNetworkRequestHandler );
interceptors = [ electronHttpsRequestInterceptor ];
var electronWebsitePageFrame = new ElectronWebsitePageFrame(interceptors);
electronWebsitePageFrame.setupProtocolInterceptors();
appWindow.on("closed", () => {
appWindow = null;
});
}
app.on("ready", createWindow);
and now if you run this and begin to make requests that are related to the in app browser the responsePreparationChain will trigger on its last element of the array and the current IncomingTrafficCounter will be logged to the console in your terminal or command prompt.
So this section stands as to point out that their is a lot of room for improvement in this library by listing out what its current limitations are will do just that and that list of current limitations can be seen below, if you feel you can address any of these limitations of this library or others note mentioned here go ahead but ensure to apply the same license when you fork the library.
-
ElectronNetworkRequestHandler, this class although it preforms its responsibilities to handle all the ins and outs of sending requests out and ensuring that responses are in an expected format for the users of the library to work with(e.g. modify) before returning to ElectronNetworkRequestInterceptor, it still currently holds alot of room for improvement, the first of which being response body conversion currently it is only designed to convert the body of a html page to text, all others will be left as ReadableStreams making it difficult for users of this library to modify different types of incoming traffic's bodies, and their is more than likely plenty more improvements just waiting to be made here.
-
WebsitePageFrame, currently the components this library provides only support smooth navigation from page to page from the child nested browsing contexts requests directly, their is the method of moving from website to website by clearing the cache each time of its current origin that is stored, so some new secure ways of moving from website to website smoothly from the child nested browsing context directly would be usefull though.
-
Framework Support, currently the list of supported frameworks that this library can be used on is limited to just the one.
this class extends MessageBuilder providing no additional member variables or methods, instead its use is during construction of an object of this type it gives predefined values to one of its existent member variables _messageSignatureGenerator for use to automatically generate a message signature when building messages to be sent by WebsitePageFrameClient sendMessage method.
None.
- public WebsitePageFrameMessageBuilder()
- protected setMessageSignatureGenerator(messageSignatureGenerator): Function
- public attachAction(action: Action): MessageBuilder
- public finishBuild(): Message
This class extends Client.
- private previousMessageSignature: int | null
- protected clientConnection: Connection | null
- public WebsitePageFrameClient()
- public WebsitePageFrameClient(websitePageFrameConnection: WebsitePageFrameConnection | null )
- public WebsitePageFrameClient(websitePageFrameConnection: WebsitePageFrameConnection | null, responseExpectedByTimeoutMilliseconds: int | null )
- private setupMessageResponseListener(): void
- public sendMessage(message: Message): void
- public sendMessagePreChecks(message: Message): boolean
- public getPreviousMessageSentSignature(): int | null
- private setPreviousMessageSentSignature(previousMessageSentSignature: int | null): void
- public isConnectionReady(): boolean
- public getClientConnection(): Connection | null
- public setClientConnection(clientConnection: Connection | null): void
- public getResponse(): Object
- protected setResponse(response: Object): void
- public getResponseExpectedByTimeoutMilliseconds(): int
- public setResponseExpectedByTimeoutMilliseconds(responseExpectedByTimeoutMilliseconds: int): void
This class extends WebsitePageFrameClient.
- protected clientConnection Connection | null
- public WebsitePageFramePingClient()
- public sendMessage(message: Message): void
- public getPreviousMessageSentSignature(): int | null
- public isConnectionReady(): boolean
- public getClientConnection(): Connection | null
- public setClientConnection(clientConnection: Connection | null): void
- public getResponse(): Object | null
- protected setResponse(response: Object | null): void
- public getResponseExpectedByTimeoutMilliseconds(): int
- public setResponseExpectedByTimeoutMilliseconds(responseExpectedByTimeoutMilliseconds: int): void
This class extends Connection.
- private WEBSITE_URL_UNIQUE_FRAGMENT: string
- private childBrowsingContextCurrentOrigin: string
- private childBrowsingContextInitialURL: string
- private childBrowsingContext: Object | null
- protected pingClient: Client
- protected connectionRetryThread
- public WebsitePageFrameConnection()
- public WebsitePageFrameConnection(childBrowsingContextOrigin: string | null)
- public WebsitePageFrameConnection(childBrowsingContextOrigin: string | null, childBrowsingContext: Object | null)
- public WebsitePageFrameConnection(childBrowsingContextOrigin: string | null, childBrowsingContext: Object | null, maxConnectionRetries: int | null)
- public openNewConnection(): void
- public closeCurrentConnection(): void
- public updateChildBrowsingContextCurrentLocationInformation(childBrowsingContextIntialURL: string): void
- public getWebsiteUrlUniqueFragment(): string
- public getBrowsingContextCurrentOrigin(): string
- public getChildBrowsingContextInitialURL(): string
- public getChildBrowsingContext(): Object | null
- public setChildBrowsingContext(childBrowsingContext: Object | null): void
- protected setPingClient(pingClient: WebsitePageFramePingClient): void
- protected setConnectionRetryThread(connectionRetryThread: BrowserThread): void
- public getIsConnectionOpen(): boolean
- protected setIsConnectionOpen(isConnectionOpen: boolean): void
- protected getPingClient(): Client
- public getConnectionRetryThread(): Thread
- public getCurrentConnectionRetriesLeft(): int
- public setCurrentConnectionRetriesLeft(currentConnectionRetriesLeft: int): void
- public getMaxConnectionRetries(): int
- public setMaxConnectionRetries(maxConnectionRetries: int): void
This class extends [Thread](#Thread).
- public BrowserThread(codeToBeExecuted: Function)
- public BrowserThread(codeToBeExecuted: Function, codeIsToBeExecutedEveryXMilliseconds: int | null)
- public start(): void
- public stop(): void
- protected setCodeToBeExecuted(functionToBeExecuted: Function): void
- private getCodeExecutingIntervalId(): number | null
- private setCodeExecutingIntervalId(codeExecutingIntervalId: number | null): void
- public getCodeIsToBeExecutedEveryXMilliseconds(): int
- public setCodeIsToBeExecutedEveryXMilliseconds(codeIsToBeExecutedEveryXMilliseconds: int): void
- public getState(): string
- protected setState(state: string): void
- protected getCodeToBeExecuted(): Object
- public Action()
- public Action(actionParameters: Array | null)
- public Action(actionParameters: Array | null, actionArguements: Array | null)
- public Action(actionParameters: Array | null, actionArguements: Array | null, actionBody: string | null)
- public getActionParameters(): Array
- public setActionParameters(actionParameters: Array): void
- public getActionArguements(): Array
- public setActionArguements(actionArguements: Array): void
- public getActionBody(): string
- public setActionBody(actionBody: string):void
- public toJsonFormat(): Object
- public fromJsonFormat(actionObject: Object): void
None.
- public Builder()
- public abstract finishBuild(): void
This class extends Builder.
- protected chain: Array
- public ChainBuilder()
- public attachChainLink(chainLink: ChainLink): ChainBuilder
- public finishBuild(): Array
This class extends ChainBuilder.
None.
- public TaskChainBuilder()
- public attachChainLink(taskChainLink: TaskChainLink): TaskChainBuilder
- public finishBuild(): Array
This class extends ChainBuilder.
None.
- public ResponsibilityChainBuilder()
- public attachChainLink(responsibilityChainLink: ResponsibilityChainLink): ResponsibilityChainBuilder
- public finishBuild(): Array
This class extends Builder.
- public MessageBuilder(messageSignatureGenerator: Function)
- private setMessageSignatureGenerator(messageSignatureGenerator: Function): void
- public attachAction(action: Action): MessageBuilder
- public finishBuild(): Message
None.
- public ChainLink()
None.
This class extends ChainLink.
None.
- public TaskChainLink()
- public abstract preformTask(): void
This class extends ChainLink.
- private taskChain: Array
- public ResponsibilityChainLink()
- public ResponsibilityChainLink(taskChainBuilder: TaskChainBuilder | null)
- public getTaskChain(): Array
- public setTaskChain(taskChainBuilder: TaskChainBuilder): void
- public abstract checkIfResponsible(): boolean
- public abstract handleResponsibility(): void
- protected clientConnection: Connection | null
- private response: Object | null
- private responseExpectedByTimeoutMilliseconds: int
- public Client()
- public Client(clientConnection: Connection | null)
- public Client(clientConnection: Connection | null, responseExpectedByTimeoutMilliseconds: int | null)
- public abstract sendMessage(message: Message): void
- public abstract sendMessagePreChecks(message: Messsage): boolean
- public isConnectionReady(): boolean
- public getClientConnection(): Connection | null
- public setClientConnection(clientConnection: Connection ): void
- public getResponse(): Object | null
- protected setResponse(response: Object | null): void
- public getResponseExpectedByTimeoutMilliseconds(): int
- public setResponseExpectedByTimeoutMilliseconds(responseExpectedByTimeoutMilliseconds: int): void
- protected pingClient: Client
- protected connectionRetryThread: Thread
- public currentConnectionRetriesLeft: int
- public maxConnectionRetries: int
- public isConnectionOpen: boolean
- public Connection(pingClient: Client, connectionRetryThread: Thread)
- public Connection(pingClient: Client, connectionRetryThread: Thread, maxConnectionRetries: int | null)
- public abstract openNewConnection(): void
- public abstract closeCurrentConnection(): void
- public getIsConnectionOpen(): boolean
- protected setIsConnectionOpen(isConnectionOpen: boolean): void
- protected getPingClient(): Client
- protected setPingClient(pingClient: Client): void
- public getConnectionRetryThread(): Thread
- protected setConnectionRetryThread(connectionRetryThread: Thread): void
- public getCurrentConnectionRetriesLeft(): int
- public setCurrentConnectionRetriesLeft(currentConnectionRetriesLeft: int): void
- public getMaxConnectionRetries(): int
- public setMaxConnectionRetries(maxConnectionRetries: int): void
None.
- public Map()
- public abstract get(key: string): void
- public abstract set(key: string, value: any): void
- public abstract remove(key: string): void
- public abstract has(key: string): boolean
- protected abstract prepareValidKey(key: string): void
- private action: Action
- private messageSignature: int
- public Message()
- public Message(action: Action | null)
- public Message(action: Action | null, messageSignature: int | null)
- public toJsonFormat(): Object
- public fromJsonFormat(jsonFormattedMessage: Object): void
- public getAction(): Action
- public setAction(action: Action): void
- public getMessageSignature(): int
- public setMessageSignature(messageSignature: int)
- public Thread(codeToBeExecuted: Object)
- public abstract start(): void
- public abstract stop(): void
- public getState(): string
- protected setState(state: string): void
- protected getCodeToBeExecuted(): Object
- protected setCodeToBeExecuted(codeToBeExecuted: Object): void
This class extends ResponseModificationChainLink.
None.
- public AddFileSchemaToFrameAnscestorsChainLink()
This class extends ResponseModficationChainLink.
- private scriptsToBeInjected: Array
- public BodyScriptInjectionChainLink(scriptsToBeInjected: Array)
- public preformTask(response: Response, cache: Cache | null): void
- public getScriptsToBeInjected(): Array
- public setScriptsToBeInjected(scriptsToBeInjected: Array): void
- private arrayIsEmpyOrOnlyContainingScripts(scriptsToBeInjected: Array): boolean
This class extends ResponseModficationChainLink.
None.
- public RemoveXFrameHeaderChainLink()
This class extends TaskChainLink.
None.
- public ResponseModificationChainLink()
This class extends ResponsibilityChainLink
None.
- public ResponsePreparationChainLink(taskChainBuilder: TaskChainBuilder | null)
- public abstract checkIfResponsible(request: Request, response: Response, cache: Cache): boolean
- public handleResponsibility(response: Response, cache: Cache): void
- public getTaskChain(): Array
- public setTaskChain(taskChainBuilder: TaskChainBuilder): void
This class extends ResponseModificationChainLink.
- public WebsitePageFrameNavigationResponsePreparation()
- public handleResponsibility(response: Response, cache: Cache): void
- public getTaskChain(): Array
- public setTaskChain(taskChainBuilder: TaskChainBuilder): void
This class extends ResponsePreparationChainLink.
None.
- public WebsitePageFrameResourceResponsePreparation()
- public handleResponsibility(response: Response, cache: Cache): void
- public getTaskChain(): Array
- public setTaskChain(taskChainBuilder: TaskChainBuilder): void
- private cache: Cache
- public ElectronCacheFactory()
- private protocol: Protocol
- public Interceptor(protocol: Protocol)
- public getProtocol(): Protocol
- public setProtocol(protocol: Protocol): void
- public abstract interceptProtocolRequest(): void
This class extends Interceptor.
- private networkRequestHandler: NetworkRequestHandler
- public NetworkRequestInterceptor(protocol: Protocol, networkRequestHandler: NetworkRequestHandler)
- public getNextworkRequestHandler(): NetworkRequestHandler
- public setNetworkRequestHandler(networkRequestHandler: NetworkRequestHandler): void
- public getProtocol(): Protocol
- public setProtocol(protocol: Protocol): void
- public abstract interceptProtocolRequest(): void
This class extends NetworkRequestInterceptor.
None.
- public ElectronNetworkRequestHandler(protocol: Protocol, networkRequestHandler: NetworkRequestHandler)
- public interceptProtocolRequest(request: Object, callback: Function):
- public completion(hasErrorOccured: Error): void
- public getProtocol(): Protocol
- public setProtocol(protocol: Protocol): void
- public getNextworkRequestHandler(): NetworkRequestHandler
- public setNetworkRequestHandler(networkRequestHandler: NetworkRequestHandler): void
This class extends Map.
None.
- public Cache()
- public get(key: string): Object
- public set(key: string, value: any): void
- public remove(key: string): void
- public has(key: string): boolean
- public clear(): void
This class extends Map.
- public headers: Object
- public HeaderMap()
- public append(header: string, headerValue: any): void
- public get(header: string): Object
- public set(header: string, headerValue: any): void
- public remove(header: string): void
- public has(header: string): boolean
- public toObject(): Object
- public fromObject(headersObject Object): void
- private headerMap: HeaderMap
- private body: any
- public getHeaderMap(): HeaderMap
- public setHeaderMap(headerMap: HeaderMap): void
- public getBody(): any
- public setBody(body: any): void
This class extends NetworkMessage.
- public Request(url: string, method: string, headerMap: HeaderMap)
- public Request(url: string, method: string, headerMap: HeaderMap, body: any)
- public getUrl(): string
- public setUrl(url: string): void
- public getMethod(): string
- public setMethod(method: string): void
- public getHeaderMap(): HeaderMap
- public setHeaderMap(headerMap: HeaderMap): void
- public getBody(): any
- public setBody(body: any): void
This class extends NetworkMessage.
- private statusCode: int
- public Response(headerMap: HeaderMap, body: any, statusCode: int)
- public getStatusCode(): int
- public setStatusCode(statusCode: int): void
- public getHeaderMap(): HeaderMap
- public setHeaderMap(headerMap: HeaderMap): void
- public getBody(): any
- public setBody(body: any): void
- public NetworkRequestHandler(networkRequester: Object)
- public NetworkRequestHandler(networkRequester: Object, cache: Cache | null)
- public NetworkRequestHandler(networkRequester: Object, cache: Cache | null, responsibilityChainBuilder: ResponsibilityChainBuilder | null)
- public abstract handleNetworkRequest(request: Request): Response
- protected findResponsePreparationChainLink(request: Request, response: Response): ResponsePreparatioChainLink | null
- public getNetworkRequester(): Object
- protected setNetworkRequester(networkRequester: Object): void
- public getCache(): Cache
- public setCache(cache: Cache): void
- public getResponsePreparationChain(): Array
- public setResponsePrepatationChain(responsibilityChainBuilder: ResponsibilityChainBuilder) void
This class extends NetworkRequestHandler.
- private browserSession: Object
- public ElectronNetworkRequestHandler(browserSession: Object)
- public ElectronNetworkRequestHandler(browserSession: Object, cache: Cache | null)
- public ElectronNetworkRequestHandler(browserSession: Object, cache: Cache | null, responsibilityChainBuilder: ResponsibilityChainBuilder | null)
- public handleNetworkRequest(request: Request): Promise
- private addSiteCookiesToRequestHeaderMap(request: Request): void
- private extractNetworkRequesterResponseHeadersToHeaderMap(responseHeaders: Object ): HeaderMap
- public getBrowserSession(): Object
- public setBrowserSession(browserSession: Object): void
- protected findResponsePreparationChainLink(request: Request, response: Response): ResponsePreparationChainLink | null
- public getNetworkRequester(): Object
- protected setNetworkRequester(networkRequester: Object): void
- public getCache(): Cache
- public setCache(cache: Cache): void
- public getResponsePreparationChain(): Array
- public setResponsePrepatationChain(responsibilityChainBuilder: ResponsibilityChainBuilder) void
- private scheme: string
- public Protocol(scheme: string)
- private scriptCode: Function
- public Script(scriptCode: Function)
This class extends Script.
None.
- public WebsitePageFrameCrossOriginMessagingScript()
- private interceptors: Array
- public WebsitePageFrame(interceptors: Array)
- public getInterceptors(): Array
- public setInterceptors(interceptors: Array): void
- protected checkIfEmptyOrContainsOnlyInterceptors(interceptors: Array): boolean
- public abstract setupProtocolInterceptors(): void
This class extends WebsitePageFrame.
None.
- public getInterceptors(): Array
- public setInterceptors(interceptors: Array): void
- protected checkIfEmptyOrContainsOnlyInterceptors(interceptors: Array): boolean
/*
* This library provides an alternative in app browser to webview by removing specific restrictions placed upon iframe and more.
* Copyright (C) 2019 Ryan Molyneux
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/