Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / templates / articles / app_codelab_webview.html
1 <h1 id="add-webview">
2   <span class="h1-step">Step 4:</span>
3   Open External Links With a Webview
4 </h1>
5
6 <p class="note">
7   <strong>Want to start fresh from here?</strong>
8   Find the previous step's code in the <a href="https://github.com/mangini/io13-codelab/archive/master.zip">reference code zip</a> under <strong><em>cheat_code > solution_for_step3</strong></em>.
9 </p>
10
11 <p>In this step, you will learn:</p>
12
13 <ul>
14   <li>How to show external web content inside your app in a secure and sandboxed way.</li>
15 </ul>
16
17 <p>
18   <em>Estimated time to complete this step: 10 minutes.</em>
19   <br>
20   To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page &#8595;</a>.
21 </p>
22
23 <h2 id="overview">Learn about the webview tag</h2>
24
25 <p>Some applications need to present external web content directly to the user but 
26 keep them inside the application experience. For example, a news aggregator 
27 might want to embed the news from external sites with all the formatting, images, 
28 and behavior of the original site. For these and other usages, Chrome Apps have 
29 a custom HTML tag called <a href="/apps/tags/webview">webview</a>.</p>
30
31 <figure>
32   <img src="{{static}}/images/app_codelab/webview-example.png" alt="The Todo app using a webview">
33 </figure>
34
35 <p class="note"><strong>Webviews are sandboxed processes:</strong>
36 The enclosing Chrome App (also known as the "embedder page")
37 cannot easily access the webview's loaded DOM.
38 You can only interact with the webview using its API.</p>
39
40 <h2 id="implement-webview">Implement the webview tag</h2>
41
42 <p>Update the Todo app to search for URLs in the todo item text and create a hyperlink.
43 The link, when clicked, opens a new Chrome App window
44 (not a browser tab) with a webview presenting the content.</p>
45
46 <h3 id="update-permissions">Update permissions</h3>
47
48 <p>In <strong><em>manifest.json</em></strong>, request the <code>webview</code> permission:</p>
49
50 <pre data-filename="manifest.json">
51 "permissions": ["storage", "alarms", "notifications"<b>, "webview"</b>],
52 </pre>
53
54 <h3 id="create-webview">Create a webview embedder page</h3>
55
56 <p>Create a new file in the root of your project folder and name it <strong><em>webview.html</em></strong>.
57 This file is a basic webpage with one <code>&lt;webview&gt;</code> tag:</p>
58
59 <pre data-filename="webview.html">
60 <b>&lt;!DOCTYPE html&gt;
61 &lt;html&gt;
62 &lt;head&gt;
63   &lt;meta charset="utf-8"&gt;
64 &lt;/head&gt;
65 &lt;body&gt;
66   &lt;webview style="width: 100%; height: 100%;"&gt;&lt;/webview&gt;
67 &lt;/body&gt;
68 &lt;/html&gt;</b>
69 </pre>
70
71 <h3 id="parse-urls">Parse for URLs in todo items</h3>
72
73 <p>At the end of <strong><em>controller.js</em></strong>, add a new method called <code>_parseForURLs()</code>:</p>
74
75 <pre data-filename="controller.js">
76   Controller.prototype._getCurrentPage = function () {
77     return document.location.hash.split('/')[1];
78   };
79
80   <b>Controller.prototype._parseForURLs = function (text) {</b>
81   <b>  var re = /(https?:\/\/[^\s"&lt;&gt;,]+)/g;</b>
82   <b>  return text.replace(re, '&lt;a href="$1" data-src="$1"&gt;$1&lt;/a&gt;');</b>
83   <b>};</b>
84
85   // Export to window
86   window.app.Controller = Controller;
87 })(window);
88 </pre>
89
90 <p>Whenever a string starting with "http://" or "https://" is found, a HTML anchor tag is created to wrap around the URL.</p>
91
92 <h3 id="render-links">Render hyperlinks in the todo list</h3>
93
94 <p>Find <code>showAll()</code> in <em>controller.js</em>. Update <code>showAll()</code>
95 to parse for links by using the <code>_parseForURLs()</code> method added previously:</p>
96
97 <pre data-filename="controller.js">
98 /**
99  * An event to fire on load. Will get all items and display them in the
100  * todo-list
101  */
102 Controller.prototype.showAll = function () {
103   this.model.read(function (data) {
104     <strike>this.$todoList.innerHTML = this.view.show(data);</strike>
105     <b>this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));</b>
106   }.bind(this));
107 };
108 </pre>
109
110 <p>Do the same for <code>showActive()</code> and <code>showCompleted()</code>:</p>
111
112 <pre data-filename="controller.js">
113 /**
114  * Renders all active tasks
115  */
116 Controller.prototype.showActive = function () {
117   this.model.read({ completed: 0 }, function (data) {
118     <strike>this.$todoList.innerHTML = this.view.show(data);</strike>
119     <b>this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));</b>
120   }.bind(this));
121 };
122
123 /**
124  * Renders all completed tasks
125  */
126 Controller.prototype.showCompleted = function () {
127   this.model.read({ completed: 1 }, function (data) {
128     <strike>this.$todoList.innerHTML = this.view.show(data);</strike>
129     <b>this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));</b>
130   }.bind(this));
131 };
132 </pre>
133
134 <p>And finally, add <code>_parseForURLs()</code> to <code>editItem()</code>:</p>
135
136 <pre data-filename="controller.js">
137 Controller.prototype.editItem = function (id, label) {
138   ...
139   var onSaveHandler = function () {
140     ...
141       // Instead of re-rendering the whole view just update
142       // this piece of it
143       <strike>label.innerHTML = value;</strike>
144       <b>label.innerHTML = this._parseForURLs(value);</b>
145     ...
146   }.bind(this);
147   ...
148 }
149 </pre>
150
151 <p>Still in <code>editItem()</code>, fix the code so that it uses the 
152 <code>innerText</code> of the label instead of the label's <code>innerHTML</code>:</p>
153
154 <pre data-filename="controller.js">
155 Controller.prototype.editItem = function (id, label) {
156   ...
157   // Get the <strike>innerHTML</strike> <b>innerText</b> of the label instead of requesting the data from the
158   // ORM. If this were a real DB this would save a lot of time and would avoid
159   // a spinner gif.
160   <strike>input.value = label.innerHTML;</strike>
161   <b>input.value = label.innerText;</b>
162   ...
163 }
164 </pre>
165
166 <h3 id="open-webview">Open new window containing webview</h3>
167
168 <p>Add a <code>_doShowUrl()</code> method to <em>controller.js</em>. This method opens a new Chrome App window
169 via <a href="/apps/app_window.html#method-create">chrome.app.window.create()</a>
170 with <em>webview.html</em> as the window source:</p>
171
172 <pre data-filename="controller.js">
173   Controller.prototype._parseForURLs = function (text) {
174     var re = /(https?:\/\/[^\s"&lt;&gt;,]+)/g;
175     return text.replace(re, '&lt;a href="$1" data-src="$1"&gt;$1&lt;/a&gt;');
176   };
177
178   <b>Controller.prototype._doShowUrl = function(e) {</b>
179   <b>  // only applies to elements with data-src attributes</b>
180   <b>  if (!e.target.hasAttribute('data-src')) {</b>
181   <b>    return;</b>
182   <b>  }</b>
183   <b>  e.preventDefault();</b>
184   <b>  var url = e.target.getAttribute('data-src');</b>
185   <b>  chrome.app.window.create(</b>
186   <b>   'webview.html',</b>
187   <b>   {hidden: true},   // only show window when webview is configured</b>
188   <b>   function(appWin) {</b>
189   <b>     appWin.contentWindow.addEventListener('DOMContentLoaded',</b>
190   <b>       function(e) {</b>
191   <b>         // when window is loaded, set webview source</b>
192   <b>         var webview = appWin.contentWindow.</b>
193   <b>              document.querySelector('webview');</b>
194   <b>         webview.src = url;</b>
195   <b>         // now we can show it:</b>
196   <b>         appWin.show();</b>
197   <b>       }</b>
198   <b>     );</b>
199   <b>   });</b>
200   <b>};</b>
201
202   // Export to window
203   window.app.Controller = Controller;
204 })(window);
205
206 </pre>
207
208 <p>In the <code>chrome.app.window.create()</code> callback, note how the webview's URL is set via the <a href="/apps/tags/webview#tag"><code>src</code> tag attribute</a>.</p>
209
210 <p>Lastly, add a click event listener inside the <code>Controller</code> constructor to call 
211 <code>doShowUrl()</code> when a user clicks on a link:</p>
212
213 <pre data-filename="controller.js">
214 function Controller(model, view) {
215   ...
216   this.router = new Router();
217   this.router.init();
218
219   <b>this.$todoList.addEventListener('click', this._doShowUrl);</b>
220
221   window.addEventListener('load', function () {
222     this._updateFilterState();
223   }.bind(this));
224   ...
225 }
226 </pre>
227
228 <h2 id="launch">Launch your finished Todo app</h2>
229
230 <p>You are done Step 4! If you reload your app and add a todo item with a full URL
231 starting with http:// or https://, you should see something like this:</p>
232
233 <figure>
234   <img src="{{static}}/images/app_codelab/step4-completed.gif" alt="The Todo app with webview"/>
235 </figure>
236
237 <h2 id="recap">For more information</h2>
238
239 <p>For more detailed information about some of the APIs introduced in this step, refer to:</p>
240
241 <ul>
242   <li>
243     <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a>
244     <a href="#update-permissions" class="anchor-link-icon" title="This feature mentioned in 'Update app permissions'">&#8593;</a>
245   </li>
246   <li>
247     <a href="/apps/tags/webview" title="Read '&lt;webview&gt; Tag' in the Chrome developer docs">&lt;webview&gt; Tag</a>
248     <a href="#overview" class="anchor-link-icon" title="This feature mentioned in 'Learn about the webview tag'">&#8593;</a>
249   </li>
250   <li>
251     <a href="/apps/app_window.html#method-create" title="Read 'chrome.app.window.create()' in the Chrome developer docs">chrome.app.window.create()</a>
252     <a href="#open-webview" class="anchor-link-icon" title="This feature mentioned in 'Open new window containing webview'">&#8593;</a>
253   </li>
254
255 </ul>
256
257 <p>Ready to continue onto the next step? Go to <a href="app_codelab_images.html">Step 5 - Add images from the web &raquo;</a></p>