Bug 968647 - Part 1. Add position change notification for IME. r=roc
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Fri, 28 Feb 2014 16:45:01 +0900
changeset 188624 9724009ba067bdaa826242f0c61e95303581be91
parent 188623 9af980ae7109fe636a8c349fd5f518d000c70be7
child 188625 d3ce99c9b61ee2c18c5a44e80726ac3057918fcc
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs968647
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 968647 - Part 1. Add position change notification for IME. r=roc
docshell/base/moz.build
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
docshell/base/nsIScrollObserver.h
dom/events/nsIMEStateManager.cpp
layout/generic/nsGfxScrollFrame.cpp
widget/nsIWidget.h
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -32,16 +32,17 @@ XPIDL_SOURCES += [
     'nsIWebPageDescriptor.idl',
 ]
 
 XPIDL_MODULE = 'docshell'
 
 EXPORTS += [
     'nsDocShellLoadTypes.h',
     'nsILinkHandler.h',
+    'nsIScrollObserver.h',
     'nsIWebShellServices.h',
     'SerializedLoadContext.h',
 ]
 
 EXPORTS.mozilla += [
     'IHistory.h',
     'LoadContext.h',
 ]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -62,16 +62,17 @@
 #include "nsWidgetsCID.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScriptChannel.h"
 #include "nsITimedChannel.h"
 #include "nsIPrivacyTransitionObserver.h"
 #include "nsIReflowObserver.h"
+#include "nsIScrollObserver.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIChannel.h"
 #include "IHistory.h"
 #include "nsViewSourceHandler.h"
 
 // we want to explore making the document own the load group
 // so we can associate the document URI with the load group.
 // until this point, we have an evil hack:
@@ -2852,16 +2853,49 @@ nsDocShell::GetCurrentDocChannel()
         nsIDocument* doc = mContentViewer->GetDocument();
         if (doc) {
             return doc->GetChannel();
         }
     }
     return nullptr;
 }
 
+NS_IMETHODIMP
+nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver)
+{
+    nsWeakPtr weakObs = do_GetWeakReference(aObserver);
+    if (!weakObs) {
+        return NS_ERROR_FAILURE;
+    }
+    return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver)
+{
+    nsWeakPtr obs = do_GetWeakReference(aObserver);
+    return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::NotifyScrollObservers()
+{
+    nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
+    while (iter.HasMore()) {
+        nsWeakPtr ref = iter.GetNext();
+        nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
+        if (obs) {
+            obs->ScrollPositionChanged();
+        } else {
+            mScrollObservers.RemoveElement(ref);
+        }
+    }
+    return NS_OK;
+}
+
 //*****************************************************************************
 // nsDocShell::nsIDocShellTreeItem
 //*****************************************************************************   
 
 NS_IMETHODIMP
 nsDocShell::GetName(nsAString& aName)
 {
     aName = mName;
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -895,16 +895,17 @@ protected:
 
 private:
     nsCString         mForcedCharset;
     nsCString         mParentCharset;
     int32_t           mParentCharsetSource;
     nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
     nsTObserverArray<nsWeakPtr> mPrivacyObservers;
     nsTObserverArray<nsWeakPtr> mReflowObservers;
+    nsTObserverArray<nsWeakPtr> mScrollObservers;
     nsCString         mOriginalUriString;
 
     // Separate function to do the actual name (i.e. not _top, _self etc.)
     // searching for FindItemWithName.
     nsresult DoFindItemWithName(const char16_t* aName,
                                 nsISupports* aRequestor,
                                 nsIDocShellTreeItem* aOriginalRequestor,
                                 nsIDocShellTreeItem** _retval);
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -36,20 +36,21 @@ interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIScriptGlobalObject;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
+interface nsIScrollObserver;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(d0eaef67-4234-47de-b05a-9c7b324eb4f4)]
+[scriptable, builtinclass, uuid(e46d924d-c20f-4add-8cf5-1e1c817b2181)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -663,16 +664,34 @@ interface nsIDocShell : nsIDocShellTreeI
    * @param end           timestamp when reflow ended, in milliseconds since
    *                      navigationStart (accurate to 1/1000 of a ms)
    */
   [noscript] void notifyReflowObservers(in bool interruptible,
                                         in DOMHighResTimeStamp start,
                                         in DOMHighResTimeStamp end);
 
   /**
+   * Add an observer to the list of parties to be notified when scroll position
+   * of some elements is changed.
+   */
+  [noscript] void addWeakScrollObserver(in nsIScrollObserver obs);
+
+  /**
+   * Add an observer to the list of parties to be notified when scroll position
+   * of some elements is changed.
+   */
+  [noscript] void removeWeakScrollObserver(in nsIScrollObserver obs);
+
+  /**
+   * Notify all attached observers that the scroll position of some element
+   * has changed.
+   */
+  [noscript] void notifyScrollObservers();
+
+  /**
    * Returns true if this docshell corresponds to an <iframe mozbrowser>.
    * (<iframe mozapp mozbrowser> is not considered a browser.)
    */
   [infallible] readonly attribute boolean isBrowserElement;
 
   /**
    * Returns true iff the docshell corresponds to an <iframe mozapp>.
    */
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsIScrollObserver.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIScrollObserver_h___
+#define nsIScrollObserver_h___
+
+#include "nsISupports.h"
+
+#define NS_ISCROLLOBSERVER_IID \
+  { 0x7c1a8b63, 0xe322, 0x4827, \
+    { 0xa4, 0xb1, 0x3b, 0x6e, 0x59, 0x03, 0x47, 0x7e } }
+
+class nsIScrollObserver : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCROLLOBSERVER_IID)
+
+  /**
+   * Called when the scroll position of some element has changed.
+   */
+  virtual void ScrollPositionChanged() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIScrollObserver, NS_ISCROLLOBSERVER_IID)
+
+#endif /* nsIScrollObserver_h___ */
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/nsIMEStateManager.cpp
@@ -30,42 +30,53 @@
 #include "nsIFormControl.h"
 #include "nsIForm.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TextEvents.h"
 #include "TextComposition.h"
 #include "mozilla/Preferences.h"
 #include "nsAsyncDOMEvent.h"
+#include "nsIDocShell.h"
+#include "nsIReflowObserver.h"
+#include "nsIScrollObserver.h"
+#include "nsWeakReference.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::widget;
 
 // nsTextStateManager notifies widget of any text and selection changes
 //  in the currently focused editor
 // sTextStateObserver points to the currently active nsTextStateManager
 // sTextStateObserver is null if there is no focused editor
 
 class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
-                                     public nsStubMutationObserver
+                                     public nsStubMutationObserver,
+                                     public nsIReflowObserver,
+                                     public nsIScrollObserver,
+                                     public nsSupportsWeakReference
 {
 public:
   nsTextStateManager()
   {
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISELECTIONLISTENER
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+  NS_DECL_NSIREFLOWOBSERVER
+
+  // nsIScrollObserver
+  virtual void ScrollPositionChanged() MOZ_OVERRIDE;
 
   void     Init(nsIWidget* aWidget,
                 nsPresContext* aPresContext,
                 nsIContent* aContent);
   void     Destroy(void);
   bool     IsManaging(nsPresContext* aPresContext, nsIContent* aContent);
   bool     IsEditorHandlingEventForComposition() const;
   bool     KeepAliveDuringDeactive() const
@@ -77,16 +88,17 @@ public:
   nsCOMPtr<nsISelection>         mSel;
   nsCOMPtr<nsIContent>           mRootContent;
   nsCOMPtr<nsINode>              mEditableNode;
 
 private:
   void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
   void ObserveEditableNode();
 
+  nsCOMPtr<nsIDocShell>          mDocShell;
   nsIMEUpdatePreference mUpdatePreference;
   uint32_t mPreAttrChangeLength;
 };
 
 /******************************************************************/
 /* nsIMEStateManager                                              */
 /******************************************************************/
 
@@ -747,16 +759,18 @@ nsTextStateManager::Init(nsIWidget* aWid
 
   // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager
   // instance via nsIMEStateManager::UpdateIMEState().  So, this
   // instance might already have been destroyed, check it.
   if (!mRootContent) {
     return;
   }
 
+  mDocShell = aPresContext->GetDocShell();
+
   ObserveEditableNode();
 }
 
 void
 nsTextStateManager::ObserveEditableNode()
 {
   MOZ_ASSERT(mSel);
   MOZ_ASSERT(mRootContent);
@@ -769,16 +783,23 @@ nsTextStateManager::ObserveEditableNode(
     nsresult rv = selPrivate->AddSelectionListener(this);
     NS_ENSURE_SUCCESS_VOID(rv);
   }
 
   if (mUpdatePreference.WantTextChange()) {
     // add text change observer
     mRootContent->AddMutationObserver(this);
   }
+
+  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
+    // Add scroll position listener and reflow observer to detect position and
+    // size changes
+    mDocShell->AddWeakScrollObserver(this);
+    mDocShell->AddWeakReflowObserver(this);
+  }
 }
 
 void
 nsTextStateManager::Destroy(void)
 {
   // If CreateTextStateManager failed, mRootContent will be null,
   // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
   if (mRootContent) {
@@ -795,18 +816,23 @@ nsTextStateManager::Destroy(void)
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
     if (selPrivate)
       selPrivate->RemoveSelectionListener(this);
   }
   mSel = nullptr;
   if (mUpdatePreference.WantTextChange() && mRootContent) {
     mRootContent->RemoveMutationObserver(this);
   }
+  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
+    mDocShell->RemoveWeakScrollObserver(this);
+    mDocShell->RemoveWeakReflowObserver(this);
+  }
   mRootContent = nullptr;
   mEditableNode = nullptr;
+  mDocShell = nullptr;
   mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
 }
 
 bool
 nsTextStateManager::IsManaging(nsPresContext* aPresContext,
                                nsIContent* aContent)
 {
   if (!mSel || !mRootContent || !mEditableNode) {
@@ -828,19 +854,22 @@ nsTextStateManager::IsEditorHandlingEven
   nsRefPtr<TextComposition> composition =
     nsIMEStateManager::GetTextCompositionFor(mWidget);
   if (!composition) {
     return false;
   }
   return composition->IsEditorHandlingEvent();
 }
 
-NS_IMPL_ISUPPORTS2(nsTextStateManager,
+NS_IMPL_ISUPPORTS5(nsTextStateManager,
                    nsIMutationObserver,
-                   nsISelectionListener)
+                   nsISelectionListener,
+                   nsIReflowObserver,
+                   nsIScrollObserver,
+                   nsISupportsWeakReference)
 
 // Helper class, used for selection change notification
 class SelectionChangeEvent : public nsRunnable {
 public:
   SelectionChangeEvent(nsTextStateManager *aDispatcher,
                        bool aCausedByComposition)
     : mDispatcher(aDispatcher)
     , mCausedByComposition(aCausedByComposition)
@@ -912,16 +941,64 @@ public:
   }
 
 private:
   nsRefPtr<nsTextStateManager> mDispatcher;
   uint32_t mStart, mOldEnd, mNewEnd;
   bool mCausedByComposition;
 };
 
+class PositionChangeEvent MOZ_FINAL : public nsRunnable
+{
+public:
+  PositionChangeEvent(nsTextStateManager* aDispatcher)
+    : mDispatcher(aDispatcher) {
+    MOZ_ASSERT(mDispatcher);
+  }
+
+  NS_IMETHOD Run() {
+    if (mDispatcher->mWidget) {
+      mDispatcher->mWidget->NotifyIME(
+        IMENotification(NOTIFY_IME_OF_POSITION_CHANGE));
+    }
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsTextStateManager> mDispatcher;
+};
+
+void
+nsTextStateManager::ScrollPositionChanged()
+{
+  if (mWidget) {
+    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
+  }
+}
+
+NS_IMETHODIMP
+nsTextStateManager::Reflow(DOMHighResTimeStamp aStart,
+                           DOMHighResTimeStamp aEnd)
+{
+  if (mWidget) {
+    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextStateManager::ReflowInterruptible(DOMHighResTimeStamp aStart,
+                                        DOMHighResTimeStamp aEnd)
+{
+  if (mWidget) {
+    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
+  }
+  return NS_OK;
+}
+
 void
 nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument,
                                          nsIContent* aContent,
                                          CharacterDataChangeInfo* aInfo)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "character data changed for non-text node");
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2080,16 +2080,18 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
     return;
   }
   PostScrollEvent();
 
   // notify the listeners.
   for (uint32_t i = 0; i < mListeners.Length(); i++) {
     mListeners[i]->ScrollPositionDidChange(pt.x, pt.y);
   }
+
+  presContext->GetDocShell()->NotifyScrollObservers();
 }
 
 static void
 AppendToTop(nsDisplayListBuilder* aBuilder, nsDisplayList* aDest,
             nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer,
             uint32_t aFlags, mozilla::layers::FrameMetrics::ViewID aScrollTargetId)
 {
   if (aSource->IsEmpty())
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -221,16 +221,17 @@ struct nsIMEUpdatePreference {
 
   typedef uint8_t Notifications;
 
   enum MOZ_ENUM_TYPE(Notifications)
   {
     NOTIFY_NOTHING                       = 0,
     NOTIFY_SELECTION_CHANGE              = 1 << 0,
     NOTIFY_TEXT_CHANGE                   = 1 << 1,
+    NOTIFY_POSITION_CHANGE               = 1 << 2,
     // Following values indicate when widget needs or doesn't need notification.
     NOTIFY_CHANGES_CAUSED_BY_COMPOSITION = 1 << 6,
     // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
     //       or more compositions are possible.  E.g., Mac and Linux (GTK).
     NOTIFY_DURING_DEACTIVE               = 1 << 7,
     // Changes are notified in following conditions if the instance is
     // just constructed.  If some platforms don't need change notifications
     // in some of following conditions, the platform should remove following
@@ -259,16 +260,21 @@ struct nsIMEUpdatePreference {
     return !!(mWantUpdates & NOTIFY_SELECTION_CHANGE);
   }
 
   bool WantTextChange() const
   {
     return !!(mWantUpdates & NOTIFY_TEXT_CHANGE);
   }
 
+  bool WantPositionChanged() const
+  {
+    return !!(mWantUpdates & NOTIFY_POSITION_CHANGE);
+  }
+
   bool WantChanges() const
   {
     return WantSelectionChange() || WantTextChange();
   }
 
   bool WantChangesCausedByComposition() const
   {
     return WantChanges() &&
@@ -487,16 +493,18 @@ enum IMEMessage MOZ_ENUM_TYPE(int8_t)
   // An editable content is losing focus
   NOTIFY_IME_OF_BLUR,
   // Selection in the focused editable content is changed
   NOTIFY_IME_OF_SELECTION_CHANGE,
   // Text in the focused editable content is changed
   NOTIFY_IME_OF_TEXT_CHANGE,
   // Composition string has been updated
   NOTIFY_IME_OF_COMPOSITION_UPDATE,
+  // Position or size of focused element may be changed.
+  NOTIFY_IME_OF_POSITION_CHANGE,
   // Request to commit current composition to IME
   // (some platforms may not support)
   REQUEST_TO_COMMIT_COMPOSITION,
   // Request to cancel current composition to IME
   // (some platforms may not support)
   REQUEST_TO_CANCEL_COMPOSITION
 };