Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: ios/web/web_state/js/resources/core.js

Issue 2807213003: Move stringify, form, navigation and scroll methods out of core.js. (Closed)
Patch Set: Re-upload patch after rebase-update in local branch Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ios/web/web_state/js/resources/common.js ('k') | ios/web/web_state/js/resources/form.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // This file adheres to closure-compiler conventions in order to enable 5 // This file adheres to closure-compiler conventions in order to enable
6 // compilation with ADVANCED_OPTIMIZATIONS. In particular, members that are to 6 // compilation with ADVANCED_OPTIMIZATIONS. In particular, members that are to
7 // be accessed externally should be specified in this['style'] as opposed to 7 // be accessed externally should be specified in this['style'] as opposed to
8 // this.style because member identifiers are minified by default. 8 // this.style because member identifiers are minified by default.
9 // See http://goo.gl/FwOgy 9 // See http://goo.gl/FwOgy
10 10
(...skipping 18 matching lines...) Expand all
29 if (__gCrWeb.common.updatePluginPlaceholders()) 29 if (__gCrWeb.common.updatePluginPlaceholders())
30 __gCrWeb.message.invokeOnHost({'command': 'addPluginPlaceholders'}); 30 __gCrWeb.message.invokeOnHost({'command': 'addPluginPlaceholders'});
31 } 31 }
32 32
33 // JavaScript errors are logged on the main application side. The handler is 33 // JavaScript errors are logged on the main application side. The handler is
34 // added ASAP to catch any errors in startup. Note this does not appear to 34 // added ASAP to catch any errors in startup. Note this does not appear to
35 // work in iOS < 5. 35 // work in iOS < 5.
36 window.addEventListener('error', function(event) { 36 window.addEventListener('error', function(event) {
37 // Sadly, event.filename and event.lineno are always 'undefined' and '0' 37 // Sadly, event.filename and event.lineno are always 'undefined' and '0'
38 // with UIWebView. 38 // with UIWebView.
39 invokeOnHost_({'command': 'window.error', 39 __gCrWeb.message.invokeOnHost(
40 'message': event.message.toString()}); 40 {'command': 'window.error', 'message': event.message.toString()});
41 }); 41 });
42 42
43 43
44 // Returns true if the top window or any frames inside contain an input 44 // Returns true if the top window or any frames inside contain an input
45 // field of type 'password'. 45 // field of type 'password'.
46 __gCrWeb['hasPasswordField'] = function() { 46 __gCrWeb['hasPasswordField'] = function() {
47 return hasPasswordField_(window); 47 return hasPasswordField_(window);
48 }; 48 };
49 49
50 // Returns a string that is formatted according to the JSON syntax rules.
51 // This is equivalent to the built-in JSON.stringify() function, but is
52 // less likely to be overridden by the website itself. This public function
53 // should not be used if spoofing it would create a security vulnerability.
54 // The |__gCrWeb| object itself does not use it; it uses its private
55 // counterpart instead.
56 // Prevents websites from changing stringify's behavior by adding the
57 // method toJSON() by temporarily removing it.
58 __gCrWeb['stringify'] = function(value) {
59 if (value === null)
60 return 'null';
61 if (value === undefined)
62 return undefined;
63 if (typeof(value.toJSON) == 'function') {
64 var originalToJSON = value.toJSON;
65 value.toJSON = undefined;
66 var stringifiedValue = __gCrWeb.common.JSONStringify(value);
67 value.toJSON = originalToJSON;
68 return stringifiedValue;
69 }
70 return __gCrWeb.common.JSONStringify(value);
71 };
72
73 /*
74 * Adds the listeners that are used to handle forms, enabling autofill and
75 * the replacement method to dismiss the keyboard needed because of the
76 * Autofill keyboard accessory.
77 */
78 function addFormEventListeners_() {
79 // Focus and input events for form elements are messaged to the main
80 // application for broadcast to CRWWebControllerObservers.
81 // This is done with a single event handler for each type being added to the
82 // main document element which checks the source element of the event; this
83 // is much easier to manage than adding handlers to individual elements.
84 var formActivity = function(evt) {
85 var srcElement = evt.srcElement;
86 var fieldName = srcElement.name || '';
87 var value = srcElement.value || '';
88
89 var msg = {
90 'command': 'form.activity',
91 'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement.form),
92 'fieldName': fieldName,
93 'type': evt.type,
94 'value': value
95 };
96 invokeOnHost_(msg);
97 };
98
99 // Focus events performed on the 'capture' phase otherwise they are often
100 // not received.
101 document.addEventListener('focus', formActivity, true);
102 document.addEventListener('blur', formActivity, true);
103 document.addEventListener('change', formActivity, true);
104
105 // Text input is watched at the bubbling phase as this seems adequate in
106 // practice and it is less obtrusive to page scripts than capture phase.
107 document.addEventListener('input', formActivity, false);
108 document.addEventListener('keyup', formActivity, false);
109 };
110 50
111 // Returns true if the supplied window or any frames inside contain an input 51 // Returns true if the supplied window or any frames inside contain an input
112 // field of type 'password'. 52 // field of type 'password'.
113 // @private 53 // @private
114 var hasPasswordField_ = function(win) { 54 var hasPasswordField_ = function(win) {
115 var doc = win.document; 55 var doc = win.document;
116 56
117 // We may will not be allowed to read the 'document' property from a frame 57 // We may will not be allowed to read the 'document' property from a frame
118 // that is in a different domain. 58 // that is in a different domain.
119 if (!doc) { 59 if (!doc) {
120 return false; 60 return false;
121 } 61 }
122 62
123 if (doc.querySelector('input[type=password]')) { 63 if (doc.querySelector('input[type=password]')) {
124 return true; 64 return true;
125 } 65 }
126 66
127 var frames = win.frames; 67 var frames = win.frames;
128 for (var i = 0; i < frames.length; i++) { 68 for (var i = 0; i < frames.length; i++) {
129 if (hasPasswordField_(frames[i])) { 69 if (hasPasswordField_(frames[i])) {
130 return true; 70 return true;
131 } 71 }
132 } 72 }
133 73
134 return false; 74 return false;
135 }; 75 };
136 76
137 function invokeOnHost_(command) {
138 __gCrWeb.message.invokeOnHost(command);
139 };
140
141 // Various aspects of global DOM behavior are overridden here.
142
143 // A popstate event needs to be fired anytime the active history entry
144 // changes without an associated document change. Either via back, forward, go
145 // navigation or by loading the URL, clicking on a link, etc.
146 __gCrWeb['dispatchPopstateEvent'] = function(stateObject) {
147 var popstateEvent = window.document.createEvent('HTMLEvents');
148 popstateEvent.initEvent('popstate', true, false);
149 if (stateObject)
150 popstateEvent.state = JSON.parse(stateObject);
151
152 // setTimeout() is used in order to return immediately. Otherwise the
153 // dispatchEvent call waits for all event handlers to return, which could
154 // cause a ReentryGuard failure.
155 window.setTimeout(function() {
156 window.dispatchEvent(popstateEvent);
157 }, 0);
158 };
159
160 // A hashchange event needs to be fired after a same-document history
161 // navigation between two URLs that are equivalent except for their fragments.
162 __gCrWeb['dispatchHashchangeEvent'] = function(oldURL, newURL) {
163 var hashchangeEvent = window.document.createEvent('HTMLEvents');
164 hashchangeEvent.initEvent('hashchange', true, false);
165 if (oldURL)
166 hashchangeEvent.oldURL = oldURL;
167 if (newURL)
168 hashchangeEvent.newURL = newURL
169
170 // setTimeout() is used in order to return immediately. Otherwise the
171 // dispatchEvent call waits for all event handlers to return, which could
172 // cause a ReentryGuard failure.
173 window.setTimeout(function() {
174 window.dispatchEvent(hashchangeEvent);
175 }, 0);
176 };
177
178 // Keep the original pushState() and replaceState() methods. It's needed to
179 // update the web view's URL and window.history.state property during history
180 // navigations that don't cause a page load.
181 var originalWindowHistoryPushState = window.history.pushState;
182 var originalWindowHistoryReplaceState = window.history.replaceState;
183 __gCrWeb['replaceWebViewURL'] = function(url, stateObject) {
184 originalWindowHistoryReplaceState.call(history, stateObject, '', url);
185 };
186
187 // Intercept window.history methods to call back/forward natively.
188 window.history.back = function() {
189 invokeOnHost_({'command': 'window.history.back'});
190 };
191 window.history.forward = function() {
192 invokeOnHost_({'command': 'window.history.forward'});
193 };
194 window.history.go = function(delta) {
195 invokeOnHost_({'command': 'window.history.go', 'value': delta | 0});
196 };
197 window.history.pushState = function(stateObject, pageTitle, pageUrl) {
198 __gCrWeb.message.invokeOnHost(
199 {'command': 'window.history.willChangeState'});
200 // Calling stringify() on undefined causes a JSON parse error.
201 var serializedState =
202 typeof(stateObject) == 'undefined' ? '' :
203 __gCrWeb.common.JSONStringify(stateObject);
204 pageUrl = pageUrl || window.location.href;
205 originalWindowHistoryPushState.call(history, stateObject,
206 pageTitle, pageUrl);
207 invokeOnHost_({'command': 'window.history.didPushState',
208 'stateObject': serializedState,
209 'baseUrl': document.baseURI,
210 'pageUrl': pageUrl.toString()});
211 };
212 window.history.replaceState = function(stateObject, pageTitle, pageUrl) {
213 __gCrWeb.message.invokeOnHost(
214 {'command': 'window.history.willChangeState'});
215
216 // Calling stringify() on undefined causes a JSON parse error.
217 var serializedState =
218 typeof(stateObject) == 'undefined' ? '' :
219 __gCrWeb.common.JSONStringify(stateObject);
220 pageUrl = pageUrl || window.location.href;
221 originalWindowHistoryReplaceState.call(history, stateObject,
222 pageTitle, pageUrl);
223 invokeOnHost_({'command': 'window.history.didReplaceState',
224 'stateObject': serializedState,
225 'baseUrl': document.baseURI,
226 'pageUrl': pageUrl.toString()});
227 };
228
229 __gCrWeb['getFullyQualifiedURL'] = function(originalURL) {
230 // A dummy anchor (never added to the document) is used to obtain the
231 // fully-qualified URL of |originalURL|.
232 var anchor = document.createElement('a');
233 anchor.href = originalURL;
234 return anchor.href;
235 };
236
237 __gCrWeb['sendFaviconsToHost'] = function() { 77 __gCrWeb['sendFaviconsToHost'] = function() {
238 __gCrWeb.message.invokeOnHost({'command': 'document.favicons', 78 __gCrWeb.message.invokeOnHost({'command': 'document.favicons',
239 'favicons': __gCrWeb.common.getFavicons()}); 79 'favicons': __gCrWeb.common.getFavicons()});
240 } 80 }
241 81
242 // Tracks whether user is in the middle of scrolling/dragging. If user is
243 // scrolling, ignore window.scrollTo() until user stops scrolling.
244 var webViewScrollViewIsDragging_ = false;
245 __gCrWeb['setWebViewScrollViewIsDragging'] = function(state) {
246 webViewScrollViewIsDragging_ = state;
247 };
248 var originalWindowScrollTo = window.scrollTo;
249 window.scrollTo = function(x, y) {
250 if (webViewScrollViewIsDragging_)
251 return;
252 originalWindowScrollTo(x, y);
253 };
254
255 window.addEventListener('hashchange', function(evt) {
256 invokeOnHost_({'command': 'window.hashchange'});
257 });
258
259 // Flush the message queue. 82 // Flush the message queue.
260 if (__gCrWeb.message) { 83 if (__gCrWeb.message) {
261 __gCrWeb.message.invokeQueues(); 84 __gCrWeb.message.invokeQueues();
262 } 85 }
263 86
264 // Capture form submit actions.
265 document.addEventListener('submit', function(evt) {
266 var action;
267 if (evt['defaultPrevented'])
268 return;
269 action = evt.target.getAttribute('action');
270 // Default action is to re-submit to same page.
271 if (!action)
272 action = document.location.href;
273 invokeOnHost_({
274 'command': 'document.submit',
275 'formName': __gCrWeb.common.getFormIdentifier(evt.srcElement),
276 'href': __gCrWeb['getFullyQualifiedURL'](action)
277 });
278 }, false);
279
280 addFormEventListeners_();
281
282 }()); // End of anonymous object 87 }()); // End of anonymous object
OLDNEW
« no previous file with comments | « ios/web/web_state/js/resources/common.js ('k') | ios/web/web_state/js/resources/form.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698