browser/metro/base/content/bindings/browser.js

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:f9b7c722296e
1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 let Cc = Components.classes;
7 let Ci = Components.interfaces;
8 let Cu = Components.utils;
9
10 Cu.import("resource://gre/modules/Services.jsm");
11 Cu.import("resource://gre/modules/FormData.jsm");
12 Cu.import("resource://gre/modules/ScrollPosition.jsm");
13 Cu.import("resource://gre/modules/Timer.jsm", this);
14
15 let WebProgressListener = {
16 _lastLocation: null,
17 _firstPaint: false,
18
19 init: function() {
20 let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
21 Ci.nsIWebProgress.NOTIFY_SECURITY |
22 Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
23 Ci.nsIWebProgress.NOTIFY_STATE_NETWORK |
24 Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
25
26 let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
27 webProgress.addProgressListener(this, flags);
28 },
29
30 onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
31 if (content != aWebProgress.DOMWindow)
32 return;
33
34 sendAsyncMessage("Content:StateChange", {
35 contentWindowId: this.contentWindowId,
36 stateFlags: aStateFlags,
37 status: aStatus
38 });
39 },
40
41 onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
42 if (content != aWebProgress.DOMWindow)
43 return;
44
45 let spec = aLocationURI ? aLocationURI.spec : "";
46 let location = spec.split("#")[0];
47
48 let charset = content.document.characterSet;
49
50 sendAsyncMessage("Content:LocationChange", {
51 contentWindowId: this.contentWindowId,
52 documentURI: aWebProgress.DOMWindow.document.documentURIObject.spec,
53 location: spec,
54 canGoBack: docShell.canGoBack,
55 canGoForward: docShell.canGoForward,
56 charset: charset.toString()
57 });
58
59 this._firstPaint = false;
60 let self = this;
61
62 // Keep track of hash changes
63 this.hashChanged = (location == this._lastLocation);
64 this._lastLocation = location;
65
66 // When a new page is loaded fire a message for the first paint
67 addEventListener("MozAfterPaint", function(aEvent) {
68 removeEventListener("MozAfterPaint", arguments.callee, true);
69
70 self._firstPaint = true;
71 let scrollOffset = ContentScroll.getScrollOffset(content);
72 sendAsyncMessage("Browser:FirstPaint", scrollOffset);
73 }, true);
74 },
75
76 onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
77 },
78
79 onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
80 if (content != aWebProgress.DOMWindow)
81 return;
82
83 let serialization = SecurityUI.getSSLStatusAsString();
84
85 sendAsyncMessage("Content:SecurityChange", {
86 contentWindowId: this.contentWindowId,
87 SSLStatusAsString: serialization,
88 state: aState
89 });
90 },
91
92 get contentWindowId() {
93 return content.QueryInterface(Ci.nsIInterfaceRequestor)
94 .getInterface(Ci.nsIDOMWindowUtils)
95 .currentInnerWindowID;
96 },
97
98 QueryInterface: function QueryInterface(aIID) {
99 if (aIID.equals(Ci.nsIWebProgressListener) ||
100 aIID.equals(Ci.nsISupportsWeakReference) ||
101 aIID.equals(Ci.nsISupports)) {
102 return this;
103 }
104
105 throw Components.results.NS_ERROR_NO_INTERFACE;
106 }
107 };
108
109 WebProgressListener.init();
110
111
112 let SecurityUI = {
113 getSSLStatusAsString: function() {
114 let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
115
116 if (status) {
117 let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
118 .getService(Ci.nsISerializationHelper);
119
120 status.QueryInterface(Ci.nsISerializable);
121 return serhelper.serializeToString(status);
122 }
123
124 return null;
125 }
126 };
127
128 let WebNavigation = {
129 _webNavigation: docShell.QueryInterface(Ci.nsIWebNavigation),
130 _timer: null,
131
132 init: function() {
133 addMessageListener("WebNavigation:GoBack", this);
134 addMessageListener("WebNavigation:GoForward", this);
135 addMessageListener("WebNavigation:GotoIndex", this);
136 addMessageListener("WebNavigation:LoadURI", this);
137 addMessageListener("WebNavigation:Reload", this);
138 addMessageListener("WebNavigation:Stop", this);
139 },
140
141 receiveMessage: function(message) {
142 switch (message.name) {
143 case "WebNavigation:GoBack":
144 this.goBack();
145 break;
146 case "WebNavigation:GoForward":
147 this.goForward();
148 break;
149 case "WebNavigation:GotoIndex":
150 this.gotoIndex(message);
151 break;
152 case "WebNavigation:LoadURI":
153 this.loadURI(message);
154 break;
155 case "WebNavigation:Reload":
156 this.reload(message);
157 break;
158 case "WebNavigation:Stop":
159 this.stop(message);
160 break;
161 }
162 },
163
164 goBack: function() {
165 if (this._webNavigation.canGoBack)
166 this._webNavigation.goBack();
167 },
168
169 goForward: function() {
170 if (this._webNavigation.canGoForward)
171 this._webNavigation.goForward();
172 },
173
174 gotoIndex: function(message) {
175 this._webNavigation.gotoIndex(message.index);
176 },
177
178 loadURI: function(message) {
179 let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
180 this._webNavigation.loadURI(message.json.uri, flags, null, null, null);
181
182 let tabData = message.json;
183 if (tabData.entries) {
184 // We are going to load from history so kill the current load. We do not
185 // want the load added to the history anyway. We reload after resetting history
186 this._webNavigation.stop(this._webNavigation.STOP_ALL);
187 this._restoreHistory(tabData, 0);
188 }
189 },
190
191 reload: function(message) {
192 let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
193 this._webNavigation.reload(flags);
194 },
195
196 stop: function(message) {
197 let flags = message.json.flags || this._webNavigation.STOP_ALL;
198 this._webNavigation.stop(flags);
199 },
200
201 _restoreHistory: function _restoreHistory(aTabData, aCount) {
202 // We need to wait for the sessionHistory to be initialized and there
203 // is no good way to do this. We'll try a wait loop like desktop
204 try {
205 if (!this._webNavigation.sessionHistory)
206 throw new Error();
207 } catch (ex) {
208 if (aCount < 10) {
209 let self = this;
210 this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
211 this._timer.initWithCallback(function(aTimer) {
212 self._timer = null;
213 self._restoreHistory(aTabData, aCount + 1);
214 }, 100, Ci.nsITimer.TYPE_ONE_SHOT);
215 return;
216 }
217 }
218
219 let history = this._webNavigation.sessionHistory;
220 if (history.count > 0)
221 history.PurgeHistory(history.count);
222 history.QueryInterface(Ci.nsISHistoryInternal);
223
224 // helper hashes for ensuring unique frame IDs and unique document
225 // identifiers.
226 let idMap = { used: {} };
227 let docIdentMap = {};
228
229 for (let i = 0; i < aTabData.entries.length; i++) {
230 if (!aTabData.entries[i].url)
231 continue;
232 history.addEntry(this._deserializeHistoryEntry(aTabData.entries[i], idMap, docIdentMap), true);
233 }
234
235 // We need to force set the active history item and cause it to reload since
236 // we stop the load above
237 let activeIndex = (aTabData.index || aTabData.entries.length) - 1;
238 history.getEntryAtIndex(activeIndex, true);
239 history.QueryInterface(Ci.nsISHistory).reloadCurrentEntry();
240 },
241
242 _deserializeHistoryEntry: function _deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) {
243 let shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].createInstance(Ci.nsISHEntry);
244
245 shEntry.setURI(Services.io.newURI(aEntry.url, null, null));
246 shEntry.setTitle(aEntry.title || aEntry.url);
247 if (aEntry.subframe)
248 shEntry.setIsSubFrame(aEntry.subframe || false);
249 shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
250 if (aEntry.contentType)
251 shEntry.contentType = aEntry.contentType;
252 if (aEntry.referrer)
253 shEntry.referrerURI = Services.io.newURI(aEntry.referrer, null, null);
254
255 if (aEntry.cacheKey) {
256 let cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32);
257 cacheKey.data = aEntry.cacheKey;
258 shEntry.cacheKey = cacheKey;
259 }
260
261 if (aEntry.ID) {
262 // get a new unique ID for this frame (since the one from the last
263 // start might already be in use)
264 let id = aIdMap[aEntry.ID] || 0;
265 if (!id) {
266 for (id = Date.now(); id in aIdMap.used; id++);
267 aIdMap[aEntry.ID] = id;
268 aIdMap.used[id] = true;
269 }
270 shEntry.ID = id;
271 }
272
273 if (aEntry.docshellID)
274 shEntry.docshellID = aEntry.docshellID;
275
276 if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
277 shEntry.stateData =
278 Cc["@mozilla.org/docshell/structured-clone-container;1"].
279 createInstance(Ci.nsIStructuredCloneContainer);
280
281 shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion);
282 }
283
284 if (aEntry.scroll) {
285 let scrollPos = aEntry.scroll.split(",");
286 scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
287 shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
288 }
289
290 let childDocIdents = {};
291 if (aEntry.docIdentifier) {
292 // If we have a serialized document identifier, try to find an SHEntry
293 // which matches that doc identifier and adopt that SHEntry's
294 // BFCacheEntry. If we don't find a match, insert shEntry as the match
295 // for the document identifier.
296 let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
297 if (!matchingEntry) {
298 matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents};
299 aDocIdentMap[aEntry.docIdentifier] = matchingEntry;
300 } else {
301 shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
302 childDocIdents = matchingEntry.childDocIdents;
303 }
304 }
305
306 if (aEntry.owner_b64) {
307 let ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
308 let binaryData = atob(aEntry.owner_b64);
309 ownerInput.setData(binaryData, binaryData.length);
310 let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIObjectInputStream);
311 binaryStream.setInputStream(ownerInput);
312 try { // Catch possible deserialization exceptions
313 shEntry.owner = binaryStream.readObject(true);
314 } catch (ex) { dump(ex); }
315 }
316
317 if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
318 for (let i = 0; i < aEntry.children.length; i++) {
319 if (!aEntry.children[i].url)
320 continue;
321
322 // We're getting sessionrestore.js files with a cycle in the
323 // doc-identifier graph, likely due to bug 698656. (That is, we have
324 // an entry where doc identifier A is an ancestor of doc identifier B,
325 // and another entry where doc identifier B is an ancestor of A.)
326 //
327 // If we were to respect these doc identifiers, we'd create a cycle in
328 // the SHEntries themselves, which causes the docshell to loop forever
329 // when it looks for the root SHEntry.
330 //
331 // So as a hack to fix this, we restrict the scope of a doc identifier
332 // to be a node's siblings and cousins, and pass childDocIdents, not
333 // aDocIdents, to _deserializeHistoryEntry. That is, we say that two
334 // SHEntries with the same doc identifier have the same document iff
335 // they have the same parent or their parents have the same document.
336
337 shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap, childDocIdents), i);
338 }
339 }
340
341 return shEntry;
342 },
343
344 sendHistory: function sendHistory() {
345 // We need to package up the session history and send it to the sessionstore
346 let entries = [];
347 let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
348 for (let i = 0; i < history.count; i++) {
349 let entry = this._serializeHistoryEntry(history.getEntryAtIndex(i, false));
350
351 // If someone directly navigates to one of these URLs and they switch to Desktop,
352 // we need to make the page load-able.
353 if (entry.url == "about:home" || entry.url == "about:start") {
354 entry.url = "about:newtab";
355 }
356 entries.push(entry);
357 }
358 let index = history.index + 1;
359 sendAsyncMessage("Content:SessionHistory", { entries: entries, index: index });
360 },
361
362 _serializeHistoryEntry: function _serializeHistoryEntry(aEntry) {
363 let entry = { url: aEntry.URI.spec };
364
365 if (Util.isURLEmpty(entry.url)) {
366 entry.title = Util.getEmptyURLTabTitle();
367 } else {
368 entry.title = aEntry.title;
369 }
370
371 if (!(aEntry instanceof Ci.nsISHEntry))
372 return entry;
373
374 let cacheKey = aEntry.cacheKey;
375 if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 && cacheKey.data != 0)
376 entry.cacheKey = cacheKey.data;
377
378 entry.ID = aEntry.ID;
379 entry.docshellID = aEntry.docshellID;
380
381 if (aEntry.referrerURI)
382 entry.referrer = aEntry.referrerURI.spec;
383
384 if (aEntry.contentType)
385 entry.contentType = aEntry.contentType;
386
387 let x = {}, y = {};
388 aEntry.getScrollPosition(x, y);
389 if (x.value != 0 || y.value != 0)
390 entry.scroll = x.value + "," + y.value;
391
392 if (aEntry.owner) {
393 try {
394 let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIObjectOutputStream);
395 let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
396 pipe.init(false, false, 0, 0xffffffff, null);
397 binaryStream.setOutputStream(pipe.outputStream);
398 binaryStream.writeCompoundObject(aEntry.owner, Ci.nsISupports, true);
399 binaryStream.close();
400
401 // Now we want to read the data from the pipe's input end and encode it.
402 let scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
403 scriptableStream.setInputStream(pipe.inputStream);
404 let ownerBytes = scriptableStream.readByteArray(scriptableStream.available());
405 // We can stop doing base64 encoding once our serialization into JSON
406 // is guaranteed to handle all chars in strings, including embedded
407 // nulls.
408 entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
409 } catch (e) { dump(e); }
410 }
411
412 entry.docIdentifier = aEntry.BFCacheEntry.ID;
413
414 if (aEntry.stateData != null) {
415 entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
416 entry.structuredCloneVersion = aEntry.stateData.formatVersion;
417 }
418
419 if (!(aEntry instanceof Ci.nsISHContainer))
420 return entry;
421
422 if (aEntry.childCount > 0) {
423 entry.children = [];
424 for (let i = 0; i < aEntry.childCount; i++) {
425 let child = aEntry.GetChildAt(i);
426 if (child)
427 entry.children.push(this._serializeHistoryEntry(child));
428 else // to maintain the correct frame order, insert a dummy entry
429 entry.children.push({ url: "about:blank" });
430
431 // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595)
432 if (/^wyciwyg:\/\//.test(entry.children[i].url)) {
433 delete entry.children;
434 break;
435 }
436 }
437 }
438
439 return entry;
440 }
441 };
442
443 WebNavigation.init();
444
445
446 let DOMEvents = {
447 _timeout: null,
448 _sessionEvents: new Set(),
449 _sessionEventMap: {"SessionStore:collectFormdata" : FormData.collect,
450 "SessionStore:collectScrollPosition" : ScrollPosition.collect},
451
452 init: function() {
453 addEventListener("DOMContentLoaded", this, false);
454 addEventListener("DOMTitleChanged", this, false);
455 addEventListener("DOMLinkAdded", this, false);
456 addEventListener("DOMWillOpenModalDialog", this, false);
457 addEventListener("DOMModalDialogClosed", this, true);
458 addEventListener("DOMWindowClose", this, false);
459 addEventListener("DOMPopupBlocked", this, false);
460 addEventListener("pageshow", this, false);
461 addEventListener("pagehide", this, false);
462
463 addEventListener("input", this, true);
464 addEventListener("change", this, true);
465 addEventListener("scroll", this, true);
466 addMessageListener("SessionStore:restoreSessionTabData", this);
467 },
468
469 receiveMessage: function(message) {
470 switch (message.name) {
471 case "SessionStore:restoreSessionTabData":
472 if (message.json.formdata)
473 FormData.restore(content, message.json.formdata);
474 if (message.json.scroll)
475 ScrollPosition.restore(content, message.json.scroll.scroll);
476 break;
477 }
478 },
479
480 handleEvent: function(aEvent) {
481 let document = content.document;
482 switch (aEvent.type) {
483 case "DOMContentLoaded":
484 if (document.documentURIObject.spec == "about:blank")
485 return;
486
487 sendAsyncMessage("DOMContentLoaded", { });
488
489 // Send the session history now too
490 WebNavigation.sendHistory();
491 break;
492
493 case "pageshow":
494 case "pagehide": {
495 if (aEvent.target.defaultView != content)
496 break;
497
498 let util = aEvent.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
499 .getInterface(Ci.nsIDOMWindowUtils);
500
501 let json = {
502 contentWindowWidth: content.innerWidth,
503 contentWindowHeight: content.innerHeight,
504 windowId: util.outerWindowID,
505 persisted: aEvent.persisted
506 };
507
508 // Clear onload focus to prevent the VKB to be shown unexpectingly
509 // but only if the location has really changed and not only the
510 // fragment identifier
511 let contentWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
512 if (!WebProgressListener.hashChanged && contentWindowID == util.currentInnerWindowID) {
513 let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
514 focusManager.clearFocus(content);
515 }
516
517 sendAsyncMessage(aEvent.type, json);
518 break;
519 }
520
521 case "DOMPopupBlocked": {
522 let util = aEvent.requestingWindow.QueryInterface(Ci.nsIInterfaceRequestor)
523 .getInterface(Ci.nsIDOMWindowUtils);
524 let json = {
525 windowId: util.outerWindowID,
526 popupWindowURI: {
527 spec: aEvent.popupWindowURI.spec,
528 charset: aEvent.popupWindowURI.originCharset
529 },
530 popupWindowFeatures: aEvent.popupWindowFeatures,
531 popupWindowName: aEvent.popupWindowName
532 };
533
534 sendAsyncMessage("DOMPopupBlocked", json);
535 break;
536 }
537
538 case "DOMTitleChanged":
539 sendAsyncMessage("DOMTitleChanged", { title: document.title });
540 break;
541
542 case "DOMLinkAdded":
543 let target = aEvent.originalTarget;
544 if (!target.href || target.disabled)
545 return;
546
547 let json = {
548 windowId: target.ownerDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
549 href: target.href,
550 charset: document.characterSet,
551 title: target.title,
552 rel: target.rel,
553 type: target.type
554 };
555
556 // rel=icon can also have a sizes attribute
557 if (target.hasAttribute("sizes"))
558 json.sizes = target.getAttribute("sizes");
559
560 sendAsyncMessage("DOMLinkAdded", json);
561 break;
562
563 case "DOMWillOpenModalDialog":
564 case "DOMModalDialogClosed":
565 case "DOMWindowClose":
566 let retvals = sendSyncMessage(aEvent.type, { });
567 for (let i in retvals) {
568 if (retvals[i].preventDefault) {
569 aEvent.preventDefault();
570 break;
571 }
572 }
573 break;
574 case "input":
575 case "change":
576 this._sessionEvents.add("SessionStore:collectFormdata");
577 this._sendUpdates();
578 break;
579 case "scroll":
580 this._sessionEvents.add("SessionStore:collectScrollPosition");
581 this._sendUpdates();
582 break;
583 }
584 },
585
586 _sendUpdates: function() {
587 if (!this._timeout) {
588 // Wait a little before sending the message to batch multiple changes.
589 this._timeout = setTimeout(function() {
590 for (let eventType of this._sessionEvents) {
591 sendAsyncMessage(eventType, {
592 data: this._sessionEventMap[eventType](content)
593 });
594 }
595 this._sessionEvents.clear();
596 clearTimeout(this._timeout);
597 this._timeout = null;
598 }.bind(this), 1000);
599 }
600 }
601 };
602
603 DOMEvents.init();
604
605 let ContentScroll = {
606 // The most recent offset set by APZC for the root scroll frame
607 _scrollOffset: { x: 0, y: 0 },
608
609 init: function() {
610 addMessageListener("Content:SetWindowSize", this);
611
612 addEventListener("pagehide", this, false);
613 addEventListener("MozScrolledAreaChanged", this, false);
614 },
615
616 getScrollOffset: function(aWindow) {
617 let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
618 let scrollX = {}, scrollY = {};
619 cwu.getScrollXY(false, scrollX, scrollY);
620 return { x: scrollX.value, y: scrollY.value };
621 },
622
623 getScrollOffsetForElement: function(aElement) {
624 if (aElement.parentNode == aElement.ownerDocument)
625 return this.getScrollOffset(aElement.ownerDocument.defaultView);
626 return { x: aElement.scrollLeft, y: aElement.scrollTop };
627 },
628
629 receiveMessage: function(aMessage) {
630 let json = aMessage.json;
631 switch (aMessage.name) {
632 case "Content:SetWindowSize": {
633 let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
634 cwu.setCSSViewport(json.width, json.height);
635 sendAsyncMessage("Content:SetWindowSize:Complete", {});
636 break;
637 }
638 }
639 },
640
641 handleEvent: function(aEvent) {
642 switch (aEvent.type) {
643 case "pagehide":
644 this._scrollOffset = { x: 0, y: 0 };
645 break;
646
647 case "MozScrolledAreaChanged": {
648 let doc = aEvent.originalTarget;
649 if (content != doc.defaultView) // We are only interested in root scroll pane changes
650 return;
651
652 sendAsyncMessage("MozScrolledAreaChanged", {
653 width: aEvent.width,
654 height: aEvent.height,
655 left: aEvent.x + content.scrollX
656 });
657
658 // Send event only after painting to make sure content views in the parent process have
659 // been updated.
660 addEventListener("MozAfterPaint", function afterPaint() {
661 removeEventListener("MozAfterPaint", afterPaint, false);
662 sendAsyncMessage("Content:UpdateDisplayPort");
663 }, false);
664
665 break;
666 }
667 }
668 }
669 };
670 this.ContentScroll = ContentScroll;
671
672 ContentScroll.init();
673
674 let ContentActive = {
675 init: function() {
676 addMessageListener("Content:Activate", this);
677 addMessageListener("Content:Deactivate", this);
678 },
679
680 receiveMessage: function(aMessage) {
681 let json = aMessage.json;
682 switch (aMessage.name) {
683 case "Content:Deactivate":
684 docShell.isActive = false;
685 let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
686 if (json.keepviewport)
687 break;
688 cwu.setDisplayPortForElement(0, 0, 0, 0, content.document.documentElement, 0);
689 break;
690
691 case "Content:Activate":
692 docShell.isActive = true;
693 break;
694 }
695 }
696 };
697
698 ContentActive.init();
699
700 /**
701 * Helper class for IndexedDB, child part. Listens using
702 * the observer service for events regarding IndexedDB
703 * prompts, and sends messages to the parent to actually
704 * show the prompts.
705 */
706 let IndexedDB = {
707 _permissionsPrompt: "indexedDB-permissions-prompt",
708 _permissionsResponse: "indexedDB-permissions-response",
709
710 _quotaPrompt: "indexedDB-quota-prompt",
711 _quotaResponse: "indexedDB-quota-response",
712 _quotaCancel: "indexedDB-quota-cancel",
713
714 waitingObservers: [],
715
716 init: function IndexedDBPromptHelper_init() {
717 let os = Services.obs;
718 os.addObserver(this, this._permissionsPrompt, false);
719 os.addObserver(this, this._quotaPrompt, false);
720 os.addObserver(this, this._quotaCancel, false);
721 addMessageListener("IndexedDB:Response", this);
722 },
723
724 observe: function IndexedDBPromptHelper_observe(aSubject, aTopic, aData) {
725 if (aTopic != this._permissionsPrompt && aTopic != this._quotaPrompt && aTopic != this._quotaCancel) {
726 throw new Error("Unexpected topic!");
727 }
728
729 let requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
730 let observer = requestor.getInterface(Ci.nsIObserver);
731
732 let contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
733 let contentDocument = contentWindow.document;
734
735 if (aTopic == this._quotaCancel) {
736 observer.observe(null, this._quotaResponse, Ci.nsIPermissionManager.UNKNOWN_ACTION);
737 return;
738 }
739
740 // Remote to parent
741 sendAsyncMessage("IndexedDB:Prompt", {
742 topic: aTopic,
743 host: contentDocument.documentURIObject.asciiHost,
744 location: contentDocument.location.toString(),
745 data: aData,
746 observerId: this.addWaitingObserver(observer)
747 });
748 },
749
750 receiveMessage: function(aMessage) {
751 let payload = aMessage.json;
752 switch (aMessage.name) {
753 case "IndexedDB:Response":
754 let observer = this.getAndRemoveWaitingObserver(payload.observerId);
755 observer.observe(null, payload.responseTopic, payload.permission);
756 }
757 },
758
759 addWaitingObserver: function(aObserver) {
760 let observerId = 0;
761 while (observerId in this.waitingObservers)
762 observerId++;
763 this.waitingObservers[observerId] = aObserver;
764 return observerId;
765 },
766
767 getAndRemoveWaitingObserver: function(aObserverId) {
768 let observer = this.waitingObservers[aObserverId];
769 delete this.waitingObservers[aObserverId];
770 return observer;
771 }
772 };
773
774 IndexedDB.init();
775

mercurial