diff --git a/mobile/chrome/content/BrowserView.js b/mobile/chrome/content/BrowserView.js
index e81f42298761..a2cbf03f88b7 100644
--- a/mobile/chrome/content/BrowserView.js
+++ b/mobile/chrome/content/BrowserView.js
@@ -332,6 +332,12 @@ BrowserView.prototype = {
this.browserToViewport(browserH),
true);
bvs.zoomChanged = true;
+
+ if (this._browser) {
+ let event = document.createEvent("Events");
+ event.initEvent("ZoomChanged", true, false);
+ this._browser.dispatchEvent(event);
+ }
}
},
@@ -401,6 +407,12 @@ BrowserView.prototype = {
*/
pauseRendering: function pauseRendering() {
this._renderMode++;
+ if (this._renderMode == 1 && this._browser) {
+ let event = document.createEvent("Events");
+ event.initEvent("RenderStateChanged", true, false);
+ event.isRendering = false;
+ this._browser.dispatchEvent(event);
+ }
},
/**
@@ -414,8 +426,16 @@ BrowserView.prototype = {
if (renderNow || this._renderMode == 0)
this.renderNow();
- if (this._renderMode == 0)
+ if (this._renderMode == 0) {
this._idleServiceObserver.resumeCrawls();
+
+ if (this._browser) {
+ let event = document.createEvent("Events");
+ event.initEvent("RenderStateChanged", true, false);
+ event.isRendering = true;
+ this._browser.dispatchEvent(event);
+ }
+ }
},
/**
diff --git a/mobile/chrome/content/Util.js b/mobile/chrome/content/Util.js
index ce3e823d7769..5da7c4aebdd8 100644
--- a/mobile/chrome/content/Util.js
+++ b/mobile/chrome/content/Util.js
@@ -45,7 +45,6 @@ let Ci = Components.interfaces;
//
let Util = {
-
bind: function bind(f, thisObj) {
return function() {
return f.apply(thisObj, arguments);
diff --git a/mobile/chrome/content/browser-ui.js b/mobile/chrome/content/browser-ui.js
index c9dd6d39c664..dbdd31bee37f 100644
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -72,6 +72,11 @@ const TOOLBARSTATE_LOADED = 2;
"gFocusManager",
"@mozilla.org/focus-manager;1",
[Ci.nsIFocusManager]
+ ],
+ [
+ "gObserverService",
+ "@mozilla.org/observer-service;1",
+ [Ci.nsIObserverService]
]
].forEach(function (service) {
let [name, contract, ifaces] = service;
@@ -201,6 +206,11 @@ var BrowserUI = {
},
_toolbarLocked: 0,
+
+ isToolbarLocked: function isToolbarLocked() {
+ return this._toolbarLocked;
+ },
+
lockToolbar: function lockToolbar() {
this._toolbarLocked++;
document.getElementById("toolbar-moveable-container").top = "0";
diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js
index 18edba337af4..56ec25f36fc9 100644
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -581,11 +581,15 @@ var Browser = {
// Force commonly used border-images into the image cache
ImagePreloader.cache();
+
+ this._pluginObserver = new PluginObserver(bv);
+ new PreferenceToggle("plugins.enabled", this._pluginObserver);
},
shutdown: function() {
this._browserView.uninit();
BrowserUI.uninit();
+ this._pluginObserver.stop();
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
@@ -730,7 +734,8 @@ var Browser = {
this._selectedTab.scrollOffset = this.getScrollboxPosition(this.contentScrollboxScroller);
}
- let firstTab = this._selectedTab == null;
+ let isFirstTab = this._selectedTab == null;
+ let lastTab = this._selectedTab;
this._selectedTab = tab;
tab.ensureBrowserExists();
@@ -742,13 +747,14 @@ var Browser = {
document.getElementById("tabs").selectedTab = tab.chromeTab;
- if (!firstTab) {
+ if (!isFirstTab) {
// Update all of our UI to reflect the new tab's location
BrowserUI.updateURI();
getIdentityHandler().checkIdentity();
let event = document.createEvent("Events");
event.initEvent("TabSelect", true, false);
+ event.lastTab = lastTab;
tab.chromeTab.dispatchEvent(event);
}
@@ -1105,8 +1111,10 @@ var Browser = {
},
getBoundingContentRect: function getBoundingContentRect(contentElem) {
- let browser = Browser._browserView.getBrowser();
-
+ let tab = Browser.getTabForDocument(contentElem.ownerDocument);
+ if (!tab)
+ return null;
+ let browser = tab.browser;
if (!browser)
return null;
@@ -1151,6 +1159,12 @@ var Browser = {
return (arguments.length > 1) ? [x - x0, y - y0] : (x - x0);
},
+ browserViewToClientRect: function browserViewToClientRect(rect) {
+ let container = document.getElementById("tile-container");
+ let containerBCR = container.getBoundingClientRect();
+ return rect.clone().translate(Math.round(containerBCR.left), Math.round(containerBCR.top));
+ },
+
/**
* turn client coordinates into page-relative ones (adjusted for
* zoom and page position)
@@ -2761,3 +2775,181 @@ var ImagePreloader = {
}
}
}
+
+/** Call obj.start() or stop() based on boolean pref. */
+function PreferenceToggle(pref, obj) {
+ // Make weak reference just to be sure there are no cycles
+ gPrefService.addObserver(pref, this, true);
+ this._obj = obj;
+ this._pref = pref;
+ this._check();
+}
+
+PreferenceToggle.prototype = {
+ QueryInterface: function QueryInterface(iid) {
+ if (iid.equals(Ci.nsIObserver) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ observe: function observe(subject, topic, data) {
+ if (topic == "nsPref:changed" && data == this._pref)
+ this._check();
+ },
+
+ _check: function _check() {
+ try {
+ let value = gPrefService.getBoolPref(this._pref);
+ value ? this._obj.start() : this._obj.stop();
+ } catch(e) {
+ this._obj.stop();
+ }
+ }
+};
+
+const nsIObjectLoadingContent = Ci.nsIObjectLoadingContent_MOZILLA_1_9_2_BRANCH || Ci.nsIObjectLoadingContent;
+
+/**
+ * Allows fast-path embed rendering by letting objects know where to absolutely
+ * render on the screen.
+ */
+function PluginObserver(bv) {
+ this._emptyRect = new Rect(0, 0, 0, 0);
+ this._contentShowing = document.getElementById("observe_contentShowing");
+ this._bv = bv;
+ this._started = false;
+}
+
+PluginObserver.prototype = {
+ /** Starts flash objects fast path. */
+ start: function() {
+ if (this._started)
+ return;
+ this._started = true;
+
+ document.getElementById("tabs-container").addEventListener("TabSelect", this, false);
+ this._contentShowing.addEventListener("broadcast", this, false);
+ let browsers = document.getElementById("browsers");
+ browsers.addEventListener("RenderStateChanged", this, false);
+ gObserverService.addObserver(this, "plugin-changed-event", false);
+
+ let browser = Browser.selectedBrowser;
+ if (browser) {
+ browser.addEventListener("ZoomChanged", this, false);
+ browser.addEventListener("MozAfterPaint", this, false);
+ }
+ },
+
+ /** Stops listening for events. */
+ stop: function() {
+ if (!this._started)
+ return;
+ this._started = false;
+
+ document.getElementById("tabs-container").removeEventListener("TabSelect", this, false);
+ this._contentShowing.removeEventListener("broadcast", this, false);
+ let browsers = document.getElementById("browsers");
+ browsers.removeEventListener("RenderStateChanged", this, false);
+ gObserverService.removeObserver(this, "plugin-changed-event");
+
+ let browser = Browser.selectedBrowser;
+ if (browser) {
+ browser.removeEventListener("ZoomChanged", this, false);
+ browser.removeEventListener("MozAfterPaint", this, false);
+ }
+ },
+
+ /** Observe listens for plugin change events and maintains an embed cache. */
+ observe: function observe(subject, topic, data) {
+ let doc = subject.ownerDocument;
+ if (data == "init") {
+ if (doc.pluginCache === undefined)
+ doc.pluginCache = [];
+ doc.pluginCache.push(subject);
+ } else if (data == "reflow") {
+ this.updateCurrentBrowser();
+ } else if (data == "destroy") {
+ if (doc.pluginCache) {
+ let index = doc.pluginCache.indexOf(subject);
+ if (index != -1)
+ doc.pluginCache.splice(index, 1);
+ }
+ }
+ },
+
+ /** Update flash objects */
+ handleEvent: function handleEvent(ev) {
+ if (ev.type == "TabSelect") {
+ if (ev.lastTab) {
+ let browser = ev.lastTab.browser;
+ let oldDoc = browser.contentDocument;
+ browser.removeEventListener("ZoomChanged", this, false);
+ browser.removeEventListener("MozAfterPaint", this, false);
+ if (oldDoc.pluginCache)
+ this.updateEmbedRegions(oldDoc.pluginCache, this._emptyRect);
+ }
+
+ let browser = Browser.selectedBrowser;
+ browser.addEventListener("ZoomChanged", this, false);
+ browser.addEventListener("MozAfterPaint", this, false);
+ }
+
+ this.updateCurrentBrowser();
+ },
+
+ /** Update the current browser's flash objects. */
+ updateCurrentBrowser: function updateCurrentBrowser() {
+ let doc = Browser.selectedTab.browser.contentDocument;
+ if (!doc.pluginCache)
+ return;
+
+ let rect = this.getCriticalRect();
+ if (rect == this._emptyRect) {
+ // Always stop rendering immediately.
+ this.updateEmbedRegions(doc.pluginCache, rect);
+ } else {
+ // Wait a moment so that any chrome redraws occur first.
+ let self = this;
+ setTimeout(function() {
+ // Recalculate critical rect so we don't render when we ought not to.
+ self.updateEmbedRegions(doc.pluginCache, self.getCriticalRect());
+ }, 0);
+ }
+ },
+
+ /** More accurate version of finding the current visible region. */
+ // XXX should visible rect call take some of these things into account?
+ getCriticalRect: function getCriticalRect() {
+ let bv = this._bv;
+ if (!bv.isRendering())
+ return this._emptyRect;
+ if (Elements.contentShowing.hasAttribute("disabled"))
+ return this._emptyRect;
+
+ let vs = bv._browserViewportState;
+ let vr = bv.getVisibleRect();
+ let crit = BrowserView.Util.visibleRectToCriticalRect(vr, vs);
+
+ if (BrowserUI.isToolbarLocked()) {
+ let urlbar = document.getElementById("toolbar-moveable-container");
+ let height = urlbar.getBoundingClientRect().height;
+ crit.top += height;
+ }
+ return crit;
+ },
+
+ /** Tell embedded objects where to absolutely render, using crit for clipping. */
+ updateEmbedRegions: function updateEmbedRegions(objects, crit) {
+ let bv = this._bv;
+ let oprivate, r, dest, clip;
+ for (let i = objects.length - 1; i >= 0; i--) {
+ r = bv.browserToViewportRect(Browser.getBoundingContentRect(objects[i]));
+ dest = Browser.browserViewToClientRect(r);
+ clip = Browser.browserViewToClientRect(r.intersect(crit)).translate(-dest.left, -dest.top);
+ oprivate = objects[i].QueryInterface(nsIObjectLoadingContent);
+ oprivate.setAbsoluteScreenPosition(Browser.contentScrollbox, dest, clip);
+ }
+ },
+};
diff --git a/mobile/chrome/content/browser.xul b/mobile/chrome/content/browser.xul
index 405ac6860103..81d733b7b7cb 100644
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -93,6 +93,10 @@
+
+
+
+