Bug 1486370 - Change nsAutoCopyListener to a static class r=smaug

nsAutoCopyListener is a singleton class but refcountable and a selection
listener.  nsFrameSelection adds it to only normal Selection when it's on
macOS or it's enabled by the pref.  Additionally, it's always first selection
listener since it's added immediately after Selection instance is created.

So, we can make it a static class, and normal Selection instance should have
a bool to decide whether it should notify nsAutoCopyListener of its changes.
Then, we can save the cost of grabbing it with local RefPtr and the virtual
call.

Additionally, this patch renames nsAutoCopyListener to mozilla::AutoCopyListener
and optimizes constructor of nsFrameSelection (using bool var cache to retrieve
the pref, avoid retrieving the pref on macOS).

Differential Revision: https://phabricator.services.mozilla.com/D4504

--HG--
rename : layout/generic/nsAutoCopyListener.h => layout/generic/AutoCopyListener.h
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2018-08-30 07:36:23 +00:00
Родитель aa3c35e3d7
Коммит be86e183fd
8 изменённых файлов: 170 добавлений и 107 удалений

Просмотреть файл

@ -10,11 +10,18 @@
#include "mozilla/dom/Selection.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/AutoCopyListener.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/SelectionBinding.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/EventStates.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/RangeBoundary.h"
#include "mozilla/Telemetry.h"
#include "nsCOMPtr.h"
#include "nsString.h"
@ -53,7 +60,6 @@
#include "nsINamed.h"
#include "nsISelectionController.h" //for the enums
#include "nsAutoCopyListener.h"
#include "SelectionChangeListener.h"
#include "nsCopySupport.h"
#include "nsIClipboard.h"
@ -62,12 +68,6 @@
#include "nsIBidiKeyboard.h"
#include "nsError.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/SelectionBinding.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Telemetry.h"
#include "nsViewManager.h"
#include "nsFocusManager.h"
@ -666,9 +666,10 @@ Selection::Selection()
, mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal)
, mCustomColors(nullptr)
, mSelectionChangeBlockerCount(0)
, mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0)
, mNotifyAutoCopy(false)
{
}
@ -678,9 +679,10 @@ Selection::Selection(nsFrameSelection* aList)
, mDirection(eDirNext)
, mSelectionType(SelectionType::eNormal)
, mCustomColors(nullptr)
, mSelectionChangeBlockerCount(0)
, mUserInitiated(false)
, mCalledByJS(false)
, mSelectionChangeBlockerCount(0)
, mNotifyAutoCopy(false)
{
}
@ -3475,8 +3477,6 @@ Selection::NotifySelectionListeners()
// If there are no selection listeners, we're done!
return NS_OK;
}
AutoTArray<nsCOMPtr<nsISelectionListener>, 8>
selectionListeners(mSelectionListeners);
nsCOMPtr<nsIDocument> doc;
nsIPresShell* ps = GetPresShell();
@ -3484,7 +3484,18 @@ Selection::NotifySelectionListeners()
doc = ps->GetDocument();
}
short reason = frameSelection->PopReason();
// We've notified all selection listeners even when some of them are removed
// (and may be destroyed) during notifying one of them. Therefore, we should
// copy all listeners to the local variable first.
AutoTArray<nsCOMPtr<nsISelectionListener>, 8>
selectionListeners(mSelectionListeners);
int16_t reason = frameSelection->PopReason();
if (mNotifyAutoCopy) {
AutoCopyListener::OnSelectionChange(doc, *this, reason);
}
for (auto& listener : selectionListeners) {
listener->NotifySelectionChanged(doc, this, reason);
}

Просмотреть файл

@ -86,6 +86,14 @@ public:
// match this up with StartBatchChanges
void EndBatchChanges(int16_t aReason = nsISelectionListener::NO_REASON);
/**
* NotifyAutoCopy() starts to notify AutoCopyListener of selection changes.
*/
void NotifyAutoCopy()
{
mNotifyAutoCopy = true;
}
nsIDocument* GetParentObject() const;
DocGroup* GetDocGroup() const;
@ -678,6 +686,10 @@ private:
SelectionType mSelectionType;
UniquePtr<SelectionCustomColors> mCustomColors;
// Non-zero if we don't want any changes we make to the selection to be
// visible to content. If non-zero, content won't be notified about changes.
uint32_t mSelectionChangeBlockerCount;
/**
* True if the current selection operation was initiated by user action.
* It determines whether we exclude -moz-user-select:none nodes or not,
@ -691,9 +703,10 @@ private:
*/
bool mCalledByJS;
// Non-zero if we don't want any changes we make to the selection to be
// visible to content. If non-zero, content won't be notified about changes.
uint32_t mSelectionChangeBlockerCount;
/**
* true if AutoCopyListner::OnSelectionChange() should be called.
*/
bool mNotifyAutoCopy;
};
// Stack-class to turn on/off selection batching.

Просмотреть файл

@ -11,7 +11,6 @@
#include "DateTimeFormat.h"
#include "nsAttrValue.h"
#include "nsAutoCopyListener.h"
#include "nsColorNames.h"
#include "nsComputedDOMStyle.h"
#include "nsContentDLF.h"
@ -355,7 +354,6 @@ nsLayoutStatics::Shutdown()
WebIDLGlobalNameHash::Shutdown();
nsListControlFrame::Shutdown();
nsXBLService::Shutdown();
nsAutoCopyListener::Shutdown();
FrameLayerBuilder::Shutdown();
CubebUtils::ShutdownLibrary();

Просмотреть файл

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 mozilla_AutoCopyListener_h
#define mozilla_AutoCopyListener_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/StaticPrefs.h"
#include "nsIClipboard.h"
namespace mozilla {
class AutoCopyListener final
{
public:
/**
* OnSelectionChange() is called when a Selection whose NotifyAutoCopy() was
* called is changed.
*
* @param aDocument The document of the Selection. May be nullptr.
* @param aSelection The selection.
* @param aReason The reasons of the change.
* See nsISelectionListener::*_REASON.
*/
static void OnSelectionChange(nsIDocument* aDocument,
dom::Selection& aSelection,
int16_t aReason);
/**
* Init() initializes all static members of this class. Should be called
* only once.
*/
static void Init(int16_t aClipboardID)
{
MOZ_ASSERT(IsValidClipboardID(aClipboardID));
static bool sInitialized = false;
if (!sInitialized && IsValidClipboardID(aClipboardID)) {
sClipboardID = aClipboardID;
sInitialized = true;
}
}
/**
* IsPrefEnabled() returns true if the pref enables auto-copy feature.
*/
static bool IsPrefEnabled()
{
return StaticPrefs::clipboard_autocopy();
}
private:
static bool IsValidClipboardID(int16_t aClipboardID)
{
return aClipboardID >= nsIClipboard::kSelectionClipboard &&
aClipboardID <= nsIClipboard::kSelectionCache;
}
static int16_t sClipboardID;
};
} // namespace mozilla
#endif // #ifndef mozilla_AutoCopyListener_h

Просмотреть файл

@ -109,6 +109,7 @@ EXPORTS += [
]
EXPORTS.mozilla += [
'AutoCopyListener.h',
'CSSAlignUtils.h',
'CSSOrderAwareFrameIterator.h',
'FrameTypeList.h',

Просмотреть файл

@ -1,53 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 nsAutoCopyListener_h_
#define nsAutoCopyListener_h_
#include "nsISelectionListener.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/Selection.h"
class nsAutoCopyListener final : public nsISelectionListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISELECTIONLISTENER
explicit nsAutoCopyListener(int16_t aClipboardID)
: mCachedClipboard(aClipboardID)
{}
void Listen(mozilla::dom::Selection *aSelection)
{
NS_ASSERTION(aSelection, "Null selection passed to Listen()");
aSelection->AddSelectionListener(this);
}
static nsAutoCopyListener* GetInstance(int16_t aClipboardID)
{
if (!sInstance) {
sInstance = new nsAutoCopyListener(aClipboardID);
NS_ADDREF(sInstance);
}
return sInstance;
}
static void Shutdown()
{
NS_IF_RELEASE(sInstance);
}
private:
~nsAutoCopyListener() {}
static nsAutoCopyListener* sInstance;
int16_t mCachedClipboard;
};
#endif

Просмотреть файл

@ -61,7 +61,6 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
#include "nsIDocument.h"
#include "nsISelectionController.h" //for the enums
#include "nsAutoCopyListener.h"
#include "SelectionChangeListener.h"
#include "nsCopySupport.h"
#include "nsIClipboard.h"
@ -70,6 +69,7 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
#include "nsIBidiKeyboard.h"
#include "nsError.h"
#include "mozilla/AutoCopyListener.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/ShadowRoot.h"
@ -301,22 +301,23 @@ nsFrameSelection::nsFrameSelection()
mDomSelections[i]->SetType(kPresentSelectionTypes[i]);
}
nsAutoCopyListener *autoCopy = nullptr;
// On macOS, cache the current selection to send to osx service menu.
#ifdef XP_MACOSX
autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionCache);
#endif
// Check to see if the autocopy pref is enabled
// and add the autocopy listener if it is
if (Preferences::GetBool("clipboard.autocopy")) {
autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionClipboard);
// On macOS, cache the current selection to send to service menu of macOS.
bool enableAutoCopy = true;
AutoCopyListener::Init(nsIClipboard::kSelectionCache);
#else // #ifdef XP_MACOSX
// Check to see if the auto-copy pref is enabled and make the normal
// Selection notifies auto-copy listener of its changes.
bool enableAutoCopy = AutoCopyListener::IsPrefEnabled();
if (enableAutoCopy) {
AutoCopyListener::Init(nsIClipboard::kSelectionClipboard);
}
#endif // #ifdef XP_MACOSX #else
if (autoCopy) {
if (enableAutoCopy) {
int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
if (mDomSelections[index]) {
autoCopy->Listen(mDomSelections[index]);
mDomSelections[index]->NotifyAutoCopy();
}
}
}
@ -2898,7 +2899,7 @@ nsFrameSelection::DisconnectFromPresShell()
* if the current selection being repainted is not an empty selection.
*
* If the current selection is empty. The current selection cache
* would be cleared by nsAutoCopyListener::NotifySelectionChanged.
* would be cleared by AutoCopyListener::OnSelectionChange().
*/
nsresult
nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(Selection* aSel)
@ -2917,11 +2918,9 @@ nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(Selection* aSel)
return NS_OK;
}
// nsAutoCopyListener
// mozilla::AutoCopyListener
nsAutoCopyListener* nsAutoCopyListener::sInstance = nullptr;
NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
int16_t AutoCopyListener::sClipboardID = -1;
/*
* What we do now:
@ -2955,40 +2954,51 @@ NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
* widget cocoa nsClipboard whenever selection changes.
*/
NS_IMETHODIMP
nsAutoCopyListener::NotifySelectionChanged(nsIDocument *aDoc,
Selection *aSel, int16_t aReason)
// static
void
AutoCopyListener::OnSelectionChange(nsIDocument* aDocument,
Selection& aSelection,
int16_t aReason)
{
if (mCachedClipboard == nsIClipboard::kSelectionCache) {
MOZ_ASSERT(IsValidClipboardID(sClipboardID));
if (sClipboardID == nsIClipboard::kSelectionCache) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
// If no active window, do nothing because a current selection changed
// cannot occur unless it is in the active window.
if (!fm->GetActiveWindow()) {
return NS_OK;
return;
}
}
if (!(aReason & nsISelectionListener::MOUSEUP_REASON ||
aReason & nsISelectionListener::SELECTALL_REASON ||
aReason & nsISelectionListener::KEYPRESS_REASON))
return NS_OK; //dont care if we are still dragging
static const int16_t kResasonsToHandle =
nsISelectionListener::MOUSEUP_REASON |
nsISelectionListener::SELECTALL_REASON |
nsISelectionListener::KEYPRESS_REASON;
if (!(aReason & kResasonsToHandle)) {
return; // Don't care if we are still dragging.
}
if (!aDoc || !aSel || aSel->IsCollapsed()) {
if (!aDocument || aSelection.IsCollapsed()) {
#ifdef DEBUG_CLIPBOARD
fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
#endif
if (sClipboardID != nsIClipboard::kSelectionCache) {
// XXX Should we clear X clipboard?
return;
}
// If on macOS, clear the current selection transferable cached
// on the parent process (nsClipboard) when the selection is empty.
if (mCachedClipboard == nsIClipboard::kSelectionCache) {
return nsCopySupport::ClearSelectionCache();
}
/* clear X clipboard? */
return NS_OK;
DebugOnly<nsresult> rv = nsCopySupport::ClearSelectionCache();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"nsCopySupport::ClearSelectionCache() failed");
return;
}
NS_ENSURE_TRUE(aDoc, NS_ERROR_FAILURE);
// call the copy code
return nsCopySupport::HTMLCopy(aSel, aDoc,
mCachedClipboard, false);
// Call the copy code.
DebugOnly<nsresult> rv =
nsCopySupport::HTMLCopy(&aSelection, aDocument, sClipboardID, false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"nsCopySupport::HTMLCopy() failed");
}

Просмотреть файл

@ -87,6 +87,22 @@ VARCACHE_PREF(
RelaxedAtomicBool, false
)
//---------------------------------------------------------------------------
// Clipboard prefs
//---------------------------------------------------------------------------
#if !defined(ANDROID) && !defined(XP_MACOSX) && defined(XP_UNIX)
# define PREF_VALUE true
#else
# define PREF_VALUE false
#endif
VARCACHE_PREF(
"clipboard.autocopy",
clipboard_autocopy,
bool, PREF_VALUE
)
#undef PREF_VALUE
//---------------------------------------------------------------------------
// DOM prefs
//---------------------------------------------------------------------------