Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / google_input_tools / src / chrome / os / inputview / dom.js
blob69ab55ebed05b4fb8f1ee7152c0b8fefd228de73
1 // Copyright 2014 The Cloud Input Tools Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 /**
16  * @fileoverview Provides common operation of dom for input tools.
17  */
20 goog.provide('i18n.input.common.dom');
22 goog.require('goog.array');
23 goog.require('goog.dom');
24 goog.require('goog.dom.TagName');
25 goog.require('goog.dom.classlist');
26 goog.require('goog.style');
27 goog.require('goog.uri.utils');
28 goog.require('i18n.input.common.GlobalSettings');
31 /**
32  * When detects whether the same domain iframe, browser will throw
33  * exceptions on accessing the cross domain iframe. Stores result to avoid to
34  * throws exception twice.
35  * Key is document uid, value is object.  ifrmae uid : true/false
36  *
37  * @type {!Object.<number, !Object.<number, boolean>>}
38  * @private
39  */
40 i18n.input.common.dom.sameDomainIframes_ = {};
43 /**
44  * Checks the given element whether is editable.
45  *
46  * @param {!Element} element The element.
47  * @return {boolean} Whether the give element is editable.
48  */
49 i18n.input.common.dom.isEditable = function(element) {
50   if (!element.tagName) {
51     return false;
52   }
54   if (element.readOnly) {
55     return false;
56   }
58   switch (element.tagName.toUpperCase()) {
59     case 'TEXTAREA':
60       return true;
61     case 'INPUT':
62       return (element.type.toUpperCase() == 'TEXT' ||
63           element.type.toUpperCase() == 'SEARCH');
64     case 'DIV':
65       return element.isContentEditable;
66     case 'IFRAME':
67       // Accessing iframe's contents or properties throws exception when the
68       // iframe is not hosted on the same domain.
69       // When it happens, ignore it and consider this iframe isn't editable.
70       /** @preserveTry */
71       try {
72         var ifdoc = i18n.input.common.dom.getSameDomainFrameDoc(element);
73         return !!ifdoc && (ifdoc.designMode &&
74             ifdoc.designMode.toUpperCase() == 'ON' ||
75             ifdoc.body && ifdoc.body.isContentEditable);
76       } catch (e) {
77         return false;
78       }
79   }
81   return false;
85 /**
86  * Sets class names to an element.
87  *
88  * @param {Element} elem Element to set class names.
89  * @param {Array.<string>} classes Class names.
90  */
91 i18n.input.common.dom.setClasses = function(elem, classes) {
92   if (elem) {
93     for (var i = 0; i < classes.length; i++) {
94       if (i == 0) {
95         goog.dom.classlist.set(elem, classes[0]);
96       } else {
97         goog.dom.classlist.add(elem, classes[i]);
98       }
99     }
100   }
105  * Check the iframe whether is the same domain as the current domain.
106  * Returns the iframe content document when it's the same domain,
107  * otherwise return null.
109  * @param {!Element} element The iframe element.
110  * @return {Document} The iframe content document.
111  */
112 i18n.input.common.dom.getSameDomainFrameDoc = function(element) {
113   var uid = goog.getUid(document);
114   var frameUid = goog.getUid(element);
115   var states = i18n.input.common.dom.sameDomainIframes_[uid];
116   if (!states) {
117     states = i18n.input.common.dom.sameDomainIframes_[uid] = {};
118   }
119   /** @preserveTry */
120   try {
121     var url = window.location.href || '';
122     //Note: cross-domain IFRAME's src can be:
123     //     http://www...
124     //     https://www....
125     //     //www.
126     // Non-cross-domain IFRAME's src can be:
127     //     javascript:...
128     //     javascript://...
129     //     abc:...
130     //     abc://...
131     //     abc//...
132     //     path/index.html
133     if (!(frameUid in states)) {
134       if (element.src) {
135         var pos = element.src.indexOf('//');
136         var protocol = pos < 0 ? 'N/A' : element.src.slice(0, pos);
137         states[frameUid] = (protocol != '' &&
138             protocol != 'http:' &&
139             protocol != 'https:' ||
140             goog.uri.utils.haveSameDomain(element.src, url));
141       } else {
142         states[frameUid] = true;
143       }
144     }
145     return states[frameUid] ? goog.dom.getFrameContentDocument(element) : null;
146   } catch (e) {
147     states[frameUid] = false;
148     return null;
149   }
154  * Gets the same domain iframe or frame document in given document, default
155  * given document is current document.
157  * @param {Document=} opt_doc The given document.
158  * @return {Array.<!Document>} The same domain iframe document.
159  */
160 i18n.input.common.dom.getSameDomainDocuments = function(opt_doc) {
161   var doc = opt_doc || document;
162   var iframes = [];
163   var rets = [];
164   goog.array.extend(iframes,
165       doc.getElementsByTagName(goog.dom.TagName.IFRAME),
166       doc.getElementsByTagName(goog.dom.TagName.FRAME));
167   goog.array.forEach(iframes, function(frame) {
168     var frameDoc = i18n.input.common.dom.getSameDomainFrameDoc(frame);
169     frameDoc && rets.push(frameDoc);
170   });
171   return rets;
176  * Create the iframe in given document or default document. Then the input tool
177  * UI element will be create inside the iframe document to avoid CSS conflict.
179  * @param {Document=} opt_doc The given document.
180  * @return {!Element} The iframe element.
181  */
182 i18n.input.common.dom.createIframeWrapper = function(opt_doc) {
183   var doc = opt_doc || document;
184   var dom = goog.dom.getDomHelper();
185   var frame = dom.createDom(goog.dom.TagName.IFRAME, {
186     'frameborder': '0',
187     'scrolling': 'no',
188     'style': 'background-color:transparent;border:0;display:none;'
189   });
190   dom.append(/** @type {!Element} */ (doc.body), frame);
191   var frameDoc = dom.getFrameContentDocument(frame);
193   var css = i18n.input.common.GlobalSettings.alternativeImageUrl ?
194       i18n.input.common.GlobalSettings.css.replace(
195       /\/\/ssl.gstatic.com\/inputtools\/images/g,
196       i18n.input.common.GlobalSettings.alternativeImageUrl) :
197       i18n.input.common.GlobalSettings.css;
198   goog.style.installStyles(
199       'html body{border:0;margin:0;padding:0} html,body{overflow:hidden}' +
200       css, /** @type {!Element} */(frameDoc.body));
201   return frame;
206  * The property need to be copied from original element to its iframe wrapper.
208  * @type {!Array.<string>}
209  * @private
210  */
211 i18n.input.common.dom.iframeWrapperProperty_ = ['box-shadow', 'z-index',
212   'margin', 'position', 'display'];
216  * Copies the necessary properties value from original element to its iframe
217  * wrapper element.
219  * @param {Element} element .
220  * @param {Element} iframe The iframe wrapper element.
221  */
222 i18n.input.common.dom.copyNecessaryStyle = function(element, iframe) {
223   goog.style.setContentBoxSize(iframe, goog.style.getSize(element));
224   goog.array.forEach(i18n.input.common.dom.iframeWrapperProperty_,
225       function(property) {
226         goog.style.setStyle(iframe, property,
227             goog.style.getComputedStyle(element, property));
228       });