Important: Chrome will be removing support for Chrome Apps on Windows, Mac, and Linux. Chrome OS will continue to support Chrome Apps. Additionally, Chrome and the Web Store will continue to support extensions on all platforms. Read the announcement and learn more about migrating your app.
Step 4: Open External Links With a Webview
Want to start fresh from here? Find the previous step's code in the reference code zip under cheat_code > solution_for_step3.
In this step, you will learn:
- How to show external web content inside your app in a secure and sandboxed way.
Estimated time to complete this step: 10 minutes.
To preview what you will complete in this step, jump down to the bottom of this page ↓.
Learn about the webview tag
Some applications need to present external web content directly to the user but keep them inside the application experience. For example, a news aggregator might want to embed the news from external sites with all the formatting, images, and behavior of the original site. For these and other usages, Chrome Apps have a custom HTML tag called webview.
Webviews are sandboxed processes: The enclosing Chrome App (also known as the "embedder page") cannot easily access the webview's loaded DOM. You can only interact with the webview using its API.
Implement the webview tag
Update the Todo app to search for URLs in the todo item text and create a hyperlink. The link, when clicked, opens a new Chrome App window (not a browser tab) with a webview presenting the content.
Update permissions
In manifest.json, request the webview
permission:
"permissions": ["storage", "alarms", "notifications", "webview"],
Create a webview embedder page
Create a new file in the root of your project folder and name it webview.html.
This file is a basic webpage with one <webview>
tag:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <webview style="width: 100%; height: 100%;"></webview> </body> </html>
Parse for URLs in todo items
At the end of controller.js, add a new method called _parseForURLs()
:
Controller.prototype._getCurrentPage = function () { return document.location.hash.split('/')[1]; }; Controller.prototype._parseForURLs = function (text) { var re = /(https?:\/\/[^\s"<>,]+)/g; return text.replace(re, '<a href="$1" data-src="$1">$1</a>'); }; // Export to window window.app.Controller = Controller; })(window);
Whenever a string starting with "http://" or "https://" is found, a HTML anchor tag is created to wrap around the URL.
Render hyperlinks in the todo list
Find showAll()
in controller.js. Update showAll()
to parse for links by using the _parseForURLs()
method added previously:
/** * An event to fire on load. Will get all items and display them in the * todo-list */ Controller.prototype.showAll = function () { this.model.read(function (data) {this.$todoList.innerHTML = this.view.show(data);this.$todoList.innerHTML = this._parseForURLs(this.view.show(data)); }.bind(this)); };
Do the same for showActive()
and showCompleted()
:
/** * Renders all active tasks */ Controller.prototype.showActive = function () { this.model.read({ completed: 0 }, function (data) {this.$todoList.innerHTML = this.view.show(data);this.$todoList.innerHTML = this._parseForURLs(this.view.show(data)); }.bind(this)); }; /** * Renders all completed tasks */ Controller.prototype.showCompleted = function () { this.model.read({ completed: 1 }, function (data) {this.$todoList.innerHTML = this.view.show(data);this.$todoList.innerHTML = this._parseForURLs(this.view.show(data)); }.bind(this)); };
And finally, add _parseForURLs()
to editItem()
:
Controller.prototype.editItem = function (id, label) { ... var onSaveHandler = function () { ... // Instead of re-rendering the whole view just update // this piece of itlabel.innerHTML = value;label.innerHTML = this._parseForURLs(value); ... }.bind(this); ... }
Still in editItem()
, fix the code so that it uses the
innerText
of the label instead of the label's innerHTML
:
Controller.prototype.editItem = function (id, label) { ... // Get theinnerHTMLinnerText of the label instead of requesting the data from the // ORM. If this were a real DB this would save a lot of time and would avoid // a spinner gif.input.value = label.innerHTML;input.value = label.innerText; ... }
Open new window containing webview
Add a _doShowUrl()
method to controller.js. This method opens a new Chrome App window
via chrome.app.window.create()
with webview.html as the window source:
Controller.prototype._parseForURLs = function (text) { var re = /(https?:\/\/[^\s"<>,]+)/g; return text.replace(re, '<a href="$1" data-src="$1">$1</a>'); }; Controller.prototype._doShowUrl = function(e) { // only applies to elements with data-src attributes if (!e.target.hasAttribute('data-src')) { return; } e.preventDefault(); var url = e.target.getAttribute('data-src'); chrome.app.window.create( 'webview.html', {hidden: true}, // only show window when webview is configured function(appWin) { appWin.contentWindow.addEventListener('DOMContentLoaded', function(e) { // when window is loaded, set webview source var webview = appWin.contentWindow. document.querySelector('webview'); webview.src = url; // now we can show it: appWin.show(); } ); }); }; // Export to window window.app.Controller = Controller; })(window);
In the chrome.app.window.create()
callback, note how the webview's URL is set via the src
tag attribute.
Lastly, add a click event listener inside the Controller
constructor to call
doShowUrl()
when a user clicks on a link:
function Controller(model, view) { ... this.router = new Router(); this.router.init(); this.$todoList.addEventListener('click', this._doShowUrl); window.addEventListener('load', function () { this._updateFilterState(); }.bind(this)); ... }
Launch your finished Todo app
You are done Step 4! If you reload your app and add a todo item with a full URL starting with http:// or https://, you should see something like this:
For more information
For more detailed information about some of the APIs introduced in this step, refer to:
Ready to continue onto the next step? Go to Step 5 - Add images from the web »