[geary/bug/728002-webkit2: 107/140] Update how ClientWebView HTML preferred size changes are sent to the app.



commit 5dfcfe4c8ea43dc66502964afefa443083da440a
Author: Michael James Gratton <mike vee net>
Date:   Tue Jan 24 19:30:12 2017 +1100

    Update how ClientWebView HTML preferred size changes are sent to the app.
    
    * ui/client-web-view.js (PageState): Use event listeners to send
      coalesced preferred height changes, rather than using polling. Call
      ::load on DOM loaded, not on complete page loaded, so in-place
      mutations take affect ASAP. Replace ::preferredHeightChanged with
      ::updatePreferredHeight method that checks that the height has changed
      before sending the message to the app.
    
    * ui/composer-web-view.js: Remove loaded event handler, it's managed by
      the base class now.
    
    * ui/conversation-web-view.js (ConversationPageState): Rename
      ::updatePreferredHeight to ::pollPreferredHeightUpdate to avoid name
      clash with parent class. Stop polling if no change has occurred after a
      number of repeated checks. Remove loaded event handler, it's managed by
      the base class now.
      (ComposerPageState::createControllableQuotes): Don't update preferred
      height at the end of the call since it will be handled by the DOM load
      event handler.

 ui/client-web-view.js       |   47 ++++++++++++++++++++++++++++++++++++------
 ui/composer-web-view.js     |    6 +----
 ui/conversation-web-view.js |   26 ++++++++---------------
 3 files changed, 50 insertions(+), 29 deletions(-)
---
diff --git a/ui/client-web-view.js b/ui/client-web-view.js
index ba224ae..4c11d39 100644
--- a/ui/client-web-view.js
+++ b/ui/client-web-view.js
@@ -17,14 +17,40 @@ PageState.prototype = {
         this.allowRemoteImages = false;
         this.isLoaded = false;
         this.hasSelection = false;
+        this.lastPreferredHeight = 0;
 
         let state = this;
-        let timeoutId = window.setInterval(function() {
-            state.preferredHeightChanged();
-            if (state.isLoaded) {
-                window.clearTimeout(timeoutId);
+
+        // Coalesce multiple calls to updatePreferredHeight using a
+        // timeout to avoid the overhead of multiple JS messages sent
+        // to the app and hence view multiple resizes being queued.
+        let queueTimeout = null;
+        let queuePreferredHeightUpdate = function() {
+            if (queueTimeout != null) {
+                clearTimeout(queueTimeout);
             }
-        }, 50);
+            queueTimeout = setTimeout(
+                function() { state.updatePreferredHeight(); }, 10
+            );
+        };
+
+        // Queues an update after the DOM has been initially loaded
+        // and had any changes made to it by derived classes.
+        document.addEventListener("DOMContentLoaded", function(e) {
+            state.loaded();
+            queuePreferredHeightUpdate();
+        });
+        // Queues updates for not only the complete document, but also
+        // for any IMG elements loaded, hence handles resizing when
+        // the user later requests remote images loading.
+        //
+        // Note also that the delay introduced here by the last call
+        // to queuePreferredHeightUpdate when the complete document is
+        // loaded seems to be important to get an acurate idea of the
+        // final document size.
+        document.addEventListener("load", function(e) {
+            queuePreferredHeightUpdate();
+        }, true);
     },
     getPreferredHeight: function() {
         return window.document.documentElement.offsetHeight;
@@ -45,13 +71,20 @@ PageState.prototype = {
     remoteImageLoadBlocked: function() {
         window.webkit.messageHandlers.remoteImageLoadBlocked.postMessage(null);
     },
-    preferredHeightChanged: function() {
+    /**
+     * Sends "preferredHeightChanged" message if it has changed.
+     */
+    updatePreferredHeight: function() {
+        let updated = false;
         let height = this.getPreferredHeight();
-        if (height > 0) {
+        if (height > 0 && height != this.lastPreferredHeight) {
+            updated = true;
+            this.lastPreferredHeight = height;
             window.webkit.messageHandlers.preferredHeightChanged.postMessage(
                 height
             );
         }
+        return updated;
     },
     selectionChanged: function() {
         let hasSelection = !window.getSelection().isCollapsed;
diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js
index 7aefa15..a81cd6f 100644
--- a/ui/composer-web-view.js
+++ b/ui/composer-web-view.js
@@ -102,8 +102,7 @@ ComposerPageState.prototype = {
         };
         this.bodyObserver.observe(this.messageBody, config);
 
-        // Chain up here so we continue to a preferred size update
-        // after munging the HTML above.
+        // Chain up
         PageState.prototype.loaded.apply(this, []);
     },
     undo: function() {
@@ -434,6 +433,3 @@ let SelectionUtil = {
 
 
 var geary = new ComposerPageState();
-window.onload = function() {
-    geary.loaded();
-};
diff --git a/ui/conversation-web-view.js b/ui/conversation-web-view.js
index 3fd6fc7..e1727e7 100644
--- a/ui/conversation-web-view.js
+++ b/ui/conversation-web-view.js
@@ -33,14 +33,11 @@ ConversationPageState.prototype = {
                 e.preventDefault();
             }
         }, true);
-
     },
     loaded: function() {
         this.updateDirection();
         this.createControllableQuotes();
         this.wrapSignature();
-        // Chain up here so we continue to a preferred size update
-        // after munging the HTML above.
         PageState.prototype.loaded.apply(this, []);
     },
     /**
@@ -55,18 +52,18 @@ ConversationPageState.prototype = {
         }
     },
     /**
-     * Starts looking for changes to the page's height.
+     * Polls for a change in the page's preferred height.
      */
-    updatePreferredHeight: function() {
-        let height = this.getPreferredHeight();
+    pollPreferredHeightUpdate: function() {
         let state = this;
+        let count = 0;
         let timeoutId = window.setInterval(function() {
-            let newHeight = state.getPreferredHeight();
-            if (height != newHeight) {
-                state.preferredHeightChanged();
+            if (state.updatePreferredHeight() || ++count >= 10) {
+                // Cancel polling when height actually changes or if
+                // no change was found after a long enough period
                 window.clearTimeout(timeoutId);
             }
-        }, 50);
+        }, 10);
     },
     /**
      * Add top level blockquotes to hide/show container.
@@ -94,7 +91,7 @@ ConversationPageState.prototype = {
                     );
                 }
 
-                let script = this;
+                let state = this;
                 function newControllerButton(styleClass, text) {
                     let button = document.createElement("BUTTON");
                     button.classList.add("geary-button");
@@ -103,7 +100,7 @@ ConversationPageState.prototype = {
                         quoteContainer.classList.toggle(
                             ConversationPageState.QUOTE_HIDE_CLASS
                         );
-                        script.updatePreferredHeight();
+                        state.pollPreferredHeightUpdate();
                     };
                     button.appendChild(document.createTextNode(text));
 
@@ -127,8 +124,6 @@ ConversationPageState.prototype = {
 
                 quoteContainer.appendChild(quoteDiv);
                 parent.insertBefore(quoteContainer, nextSibling);
-
-                this.updatePreferredHeight();
             }
         }
     },
@@ -316,6 +311,3 @@ ConversationPageState.isDescendantOf = function(node, ancestorTag) {
 };
 
 var geary = new ConversationPageState();
-window.onload = function() {
-    geary.loaded();
-};


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]