Bug 1406358 - Keep SpeechDelegate object until speaking is finished. r=eeejay, a=ritu
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Thu, 12 Oct 2017 17:17:38 +0900
changeset 435304 24b1d36cd71070d9a8b7b6b719139351d62f5167
parent 435303 ba08d4e02e0c3839aa85985c7fcdf68bba94dac5
child 435305 39275addee5933725bdaf7f5de328e47df39790a
push id1567
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 12:36:05 +0000
treeherdermozilla-release@e512c14a0406 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerseeejay, ritu
bugs1406358
milestone57.0
Bug 1406358 - Keep SpeechDelegate object until speaking is finished. r=eeejay, a=ritu The delegate property of NSSpeechSynthesizer doesn't seem to add refcount from 10.13, so we have to keep SpeechDelegate object until speech is finished. MozReview-Commit-ID: EVtMOPytkjR
dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
@@ -12,71 +12,122 @@
 #include "mozilla/dom/nsSynthVoiceRegistry.h"
 #include "mozilla/dom/nsSpeechTask.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Assertions.h"
 #include "OSXSpeechSynthesizerService.h"
 
 #import <Cocoa/Cocoa.h>
 
+@class SpeechDelegate;
+
 // We can escape the default delimiters ("[[" and "]]") by temporarily
 // changing the delimiters just before they appear, and changing them back
 // just after.
 #define DLIM_ESCAPE_START "[[dlim (( ))]]"
 #define DLIM_ESCAPE_END "((dlim [[ ]]))"
 
 using namespace mozilla;
 
 class SpeechTaskCallback final : public nsISpeechTaskCallback
 {
 public:
   SpeechTaskCallback(nsISpeechTask* aTask,
                      NSSpeechSynthesizer* aSynth,
-                     const nsTArray<size_t>& aOffsets)
-    : mTask(aTask)
-    , mSpeechSynthesizer(aSynth)
-    , mOffsets(aOffsets)
-  {
-    mStartingTime = TimeStamp::Now();
-  }
+                     const nsTArray<size_t>& aOffsets);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SpeechTaskCallback, nsISpeechTaskCallback)
 
   NS_DECL_NSISPEECHTASKCALLBACK
 
   void OnWillSpeakWord(uint32_t aIndex, uint32_t aLength);
   void OnError(uint32_t aIndex);
   void OnDidFinishSpeaking();
 
 private:
-  virtual ~SpeechTaskCallback()
-  {
-    [mSpeechSynthesizer release];
-  }
+  virtual ~SpeechTaskCallback();
 
   float GetTimeDurationFromStart();
 
   nsCOMPtr<nsISpeechTask> mTask;
   NSSpeechSynthesizer* mSpeechSynthesizer;
+  SpeechDelegate* mDelegate;
   TimeStamp mStartingTime;
   uint32_t mCurrentIndex;
   nsTArray<size_t> mOffsets;
 };
 
+@interface SpeechDelegate : NSObject<NSSpeechSynthesizerDelegate>
+{
+@private
+  SpeechTaskCallback* mCallback;
+}
+
+  - (id)initWithCallback:(SpeechTaskCallback*)aCallback;
+@end
+
+@implementation SpeechDelegate
+- (id)initWithCallback:(SpeechTaskCallback*)aCallback
+{
+  [super init];
+  mCallback = aCallback;
+  return self;
+}
+
+- (void)speechSynthesizer:(NSSpeechSynthesizer *)aSender
+            willSpeakWord:(NSRange)aRange ofString:(NSString*)aString
+{
+  mCallback->OnWillSpeakWord(aRange.location, aRange.length);
+}
+
+- (void)speechSynthesizer:(NSSpeechSynthesizer *)aSender
+        didFinishSpeaking:(BOOL)aFinishedSpeaking
+{
+  mCallback->OnDidFinishSpeaking();
+}
+
+- (void)speechSynthesizer:(NSSpeechSynthesizer*)aSender
+ didEncounterErrorAtIndex:(NSUInteger)aCharacterIndex
+                 ofString:(NSString*)aString
+                  message:(NSString*)aMessage
+{
+  mCallback->OnError(aCharacterIndex);
+}
+@end
+
 NS_IMPL_CYCLE_COLLECTION(SpeechTaskCallback, mTask);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechTaskCallback)
   NS_INTERFACE_MAP_ENTRY(nsISpeechTaskCallback)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTaskCallback)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechTaskCallback)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechTaskCallback)
 
+SpeechTaskCallback::SpeechTaskCallback(nsISpeechTask* aTask,
+                                       NSSpeechSynthesizer* aSynth,
+                                       const nsTArray<size_t>& aOffsets)
+  : mTask(aTask)
+  , mSpeechSynthesizer(aSynth)
+  , mOffsets(aOffsets)
+{
+  mDelegate = [[SpeechDelegate alloc] initWithCallback:this];
+  [mSpeechSynthesizer setDelegate:mDelegate];
+  mStartingTime = TimeStamp::Now();
+}
+
+SpeechTaskCallback::~SpeechTaskCallback()
+{
+  [mSpeechSynthesizer setDelegate:nil];
+  [mDelegate release];
+  [mSpeechSynthesizer release];
+}
+
 NS_IMETHODIMP
 SpeechTaskCallback::OnCancel()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   [mSpeechSynthesizer stopSpeaking];
   return NS_OK;
 
@@ -161,54 +212,16 @@ void
 SpeechTaskCallback::OnDidFinishSpeaking()
 {
   mTask->DispatchEnd(GetTimeDurationFromStart(), mCurrentIndex);
   // no longer needed
   [mSpeechSynthesizer setDelegate:nil];
   mTask = nullptr;
 }
 
-@interface SpeechDelegate : NSObject<NSSpeechSynthesizerDelegate>
-{
-@private
-  SpeechTaskCallback* mCallback;
-}
-
-  - (id)initWithCallback:(SpeechTaskCallback*)aCallback;
-@end
-
-@implementation SpeechDelegate
-- (id)initWithCallback:(SpeechTaskCallback*)aCallback
-{
-  [super init];
-  mCallback = aCallback;
-  return self;
-}
-
-- (void)speechSynthesizer:(NSSpeechSynthesizer *)aSender
-            willSpeakWord:(NSRange)aRange ofString:(NSString*)aString
-{
-  mCallback->OnWillSpeakWord(aRange.location, aRange.length);
-}
-
-- (void)speechSynthesizer:(NSSpeechSynthesizer *)aSender
-        didFinishSpeaking:(BOOL)aFinishedSpeaking
-{
-  mCallback->OnDidFinishSpeaking();
-}
-
-- (void)speechSynthesizer:(NSSpeechSynthesizer*)aSender
- didEncounterErrorAtIndex:(NSUInteger)aCharacterIndex
-                 ofString:(NSString*)aString
-                  message:(NSString*)aMessage
-{
-  mCallback->OnError(aCharacterIndex);
-}
-@end
-
 namespace mozilla {
 namespace dom {
 
 struct OSXVoice
 {
   OSXVoice() : mIsDefault(false)
   {
   }
@@ -424,20 +437,16 @@ OSXSpeechSynthesizerService::Speak(const
       offsets.AppendElement(i);
     }
   }
 
   RefPtr<SpeechTaskCallback> callback = new SpeechTaskCallback(aTask, synth, offsets);
   nsresult rv = aTask->Setup(callback, 0, 0, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  SpeechDelegate* delegate = [[SpeechDelegate alloc] initWithCallback:callback];
-  [synth setDelegate:delegate];
-  [delegate release ];
-
   NSString* text = nsCocoaUtils::ToNSString(escapedText);
   BOOL success = [synth startSpeakingString:text];
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
   aTask->DispatchStart();
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;