<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[176785] trunk/Examples</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/176785">176785</a></dd>
<dt>Author</dt> <dd>adachan@apple.com</dd>
<dt>Date</dt> <dd>2014-12-03 22:42:38 -0800 (Wed, 03 Dec 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Add a sample plugin to demonstrate the mute plugin API.
https://bugs.webkit.org/show_bug.cgi?id=139231
Reviewed by Anders Carlsson.
* NetscapeMuteAPIPlugin/AudioPlayer.h: Added.
* NetscapeMuteAPIPlugin/AudioPlayer.mm: Added.
(-[AudioPlayer initWithURL:]):
(-[AudioPlayer dealloc]):
(-[AudioPlayer isReadyToPlay]):
(-[AudioPlayer isPlaying]):
(-[AudioPlayer setPlaying:]):
(-[AudioPlayer isMuted]):
(-[AudioPlayer setMuted:]):
(-[AudioPlayer observeValueForKeyPath:ofObject:change:context:]):
* NetscapeMuteAPIPlugin/English.lproj/InfoPlist.strings: Added.
* NetscapeMuteAPIPlugin/Info.plist: Added.
* NetscapeMuteAPIPlugin/MenuHandler.h: Added.
* NetscapeMuteAPIPlugin/MenuHandler.m: Added.
(-[MenuHandler _play:]):
(-[MenuHandler _pause:]):
(-[MenuHandler validateUserInterfaceItem:]):
(-[MenuHandler initWithAudioPlayer:]):
(-[MenuHandler dealloc]):
(-[MenuHandler menu]):
* NetscapeMuteAPIPlugin/NetscapeMuteAPIPlugin.xcodeproj/project.pbxproj: Added.
* NetscapeMuteAPIPlugin/main.m: Added.
(-[PluginObject initWithNPP:audioURL:]):
(-[PluginObject dealloc]):
(-[PluginObject menuHandler]):
(-[PluginObject isPlayingAudio]):
(-[PluginObject isMuted]):
(-[PluginObject setMuted:]):
(-[PluginObject readyStateDidChangeForAudioPlayer:]):
(-[PluginObject mutedStateDidChangeForAudioPlayer:]):
(-[PluginObject playStateDidChangeForAudioPlayer:]):
(-[PluginObject _invalidateDisplayString]):
(NP_Initialize):
(NP_GetEntryPoints):
(NP_Shutdown):
(NPP_New):
(NPP_Destroy):
(NPP_SetWindow):
(NPP_NewStream):
(NPP_DestroyStream):
(NPP_WriteReady):
(NPP_Write):
(NPP_StreamAsFile):
(NPP_Print):
(handleDraw):
(invalidatePlugin):
(handleMouseEvent):
(NPP_HandleEvent):
(NPP_URLNotify):
(NPP_GetValue):
(NPP_SetValue):
* NetscapeMuteAPIPlugin/test.html: Added.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkExamplesChangeLog">trunk/Examples/ChangeLog</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li>trunk/Examples/NetscapeMuteAPIPlugin/</li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginAudioPlayerh">trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.h</a></li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginAudioPlayermm">trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.mm</a></li>
<li>trunk/Examples/NetscapeMuteAPIPlugin/English.lproj/</li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginEnglishlprojInfoPliststrings">trunk/Examples/NetscapeMuteAPIPlugin/English.lproj/InfoPlist.strings</a></li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginInfoplist">trunk/Examples/NetscapeMuteAPIPlugin/Info.plist</a></li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginMenuHandlerh">trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.h</a></li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginMenuHandlerm">trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.m</a></li>
<li>trunk/Examples/NetscapeMuteAPIPlugin/NetscapeMuteAPIPlugin.xcodeproj/</li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginNetscapeMuteAPIPluginxcodeprojprojectpbxproj">trunk/Examples/NetscapeMuteAPIPlugin/NetscapeMuteAPIPlugin.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkExamplesNetscapeMuteAPIPluginmainm">trunk/Examples/NetscapeMuteAPIPlugin/main.m</a></li>
<li><a href="#trunkExamplesNetscapeMuteAPIPlugintesthtml">trunk/Examples/NetscapeMuteAPIPlugin/test.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkExamplesChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Examples/ChangeLog (176784 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/ChangeLog        2014-12-04 03:12:06 UTC (rev 176784)
+++ trunk/Examples/ChangeLog        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -1,3 +1,63 @@
</span><ins>+2014-12-03 Ada Chan <adachan@apple.com>
+
+ Add a sample plugin to demonstrate the mute plugin API.
+ https://bugs.webkit.org/show_bug.cgi?id=139231
+
+ Reviewed by Anders Carlsson.
+
+ * NetscapeMuteAPIPlugin/AudioPlayer.h: Added.
+ * NetscapeMuteAPIPlugin/AudioPlayer.mm: Added.
+ (-[AudioPlayer initWithURL:]):
+ (-[AudioPlayer dealloc]):
+ (-[AudioPlayer isReadyToPlay]):
+ (-[AudioPlayer isPlaying]):
+ (-[AudioPlayer setPlaying:]):
+ (-[AudioPlayer isMuted]):
+ (-[AudioPlayer setMuted:]):
+ (-[AudioPlayer observeValueForKeyPath:ofObject:change:context:]):
+ * NetscapeMuteAPIPlugin/English.lproj/InfoPlist.strings: Added.
+ * NetscapeMuteAPIPlugin/Info.plist: Added.
+ * NetscapeMuteAPIPlugin/MenuHandler.h: Added.
+ * NetscapeMuteAPIPlugin/MenuHandler.m: Added.
+ (-[MenuHandler _play:]):
+ (-[MenuHandler _pause:]):
+ (-[MenuHandler validateUserInterfaceItem:]):
+ (-[MenuHandler initWithAudioPlayer:]):
+ (-[MenuHandler dealloc]):
+ (-[MenuHandler menu]):
+ * NetscapeMuteAPIPlugin/NetscapeMuteAPIPlugin.xcodeproj/project.pbxproj: Added.
+ * NetscapeMuteAPIPlugin/main.m: Added.
+ (-[PluginObject initWithNPP:audioURL:]):
+ (-[PluginObject dealloc]):
+ (-[PluginObject menuHandler]):
+ (-[PluginObject isPlayingAudio]):
+ (-[PluginObject isMuted]):
+ (-[PluginObject setMuted:]):
+ (-[PluginObject readyStateDidChangeForAudioPlayer:]):
+ (-[PluginObject mutedStateDidChangeForAudioPlayer:]):
+ (-[PluginObject playStateDidChangeForAudioPlayer:]):
+ (-[PluginObject _invalidateDisplayString]):
+ (NP_Initialize):
+ (NP_GetEntryPoints):
+ (NP_Shutdown):
+ (NPP_New):
+ (NPP_Destroy):
+ (NPP_SetWindow):
+ (NPP_NewStream):
+ (NPP_DestroyStream):
+ (NPP_WriteReady):
+ (NPP_Write):
+ (NPP_StreamAsFile):
+ (NPP_Print):
+ (handleDraw):
+ (invalidatePlugin):
+ (handleMouseEvent):
+ (NPP_HandleEvent):
+ (NPP_URLNotify):
+ (NPP_GetValue):
+ (NPP_SetValue):
+ * NetscapeMuteAPIPlugin/test.html: Added.
+
</ins><span class="cx"> 2014-03-14 Maciej Stachowiak <mjs@apple.com>
</span><span class="cx">
</span><span class="cx"> Replace "Apple Computer, Inc." with "Apple Inc." in copyright headers
</span></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginAudioPlayerh"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.h (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.h         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.h        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,62 @@
</span><ins>+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebKit/npfunctions.h>
+
+@class AVPlayer;
+@class AVPlayerItem;
+@class AudioPlayer;
+
+@protocol AudioPlayerDelegate <NSObject>
+
+- (void)readyStateDidChangeForAudioPlayer:(AudioPlayer *)audioPlayer;
+- (void)mutedStateDidChangeForAudioPlayer:(AudioPlayer *)audioPlayer;
+- (void)playStateDidChangeForAudioPlayer:(AudioPlayer *)audioPlayer;
+
+@end
+
+@interface AudioPlayer : NSObject {
+ BOOL _playing;
+ AVPlayer *_audioPlayer;
+ AVPlayerItem *_playerItem;
+ id<AudioPlayerDelegate> _audioPlayerDelegate;
+}
+
+- (instancetype)initWithURL:(NSURL *)url;
+
+@property (nonatomic, weak) id<AudioPlayerDelegate> audioPlayerDelegate;
+@property (nonatomic, readonly, getter=isReadyToPlay) BOOL readyToPlay;
+@property (nonatomic, getter=isPlaying) BOOL playing;
+@property (nonatomic, getter=isMuted) BOOL muted;
+
+@end
</ins></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginAudioPlayermm"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.mm (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.mm         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/AudioPlayer.mm        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,118 @@
</span><ins>+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "AudioPlayer.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+static void *itemStatusKVOContext = &itemStatusKVOContext;
+
+@implementation AudioPlayer
+
+@synthesize audioPlayerDelegate=_audioPlayerDelegate;
+
+- (instancetype)initWithURL:(NSURL *)url
+{
+ if (!url.absoluteString.length)
+ return nil;
+
+ if (!(self = [super init]))
+ return nil;
+
+ AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
+ _playerItem = [[AVPlayerItem alloc] initWithAsset:avAsset];
+ [_playerItem addObserver:self forKeyPath:@"status" options:0 context:itemStatusKVOContext];
+ _audioPlayer = [[AVPlayer alloc] initWithPlayerItem:_playerItem];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_playerItem removeObserver:self forKeyPath:@"status"];
+ [_audioPlayer release];
+ [_playerItem release];
+ [super dealloc];
+}
+
+- (BOOL)isReadyToPlay
+{
+ return _audioPlayer.currentItem.status == AVPlayerItemStatusReadyToPlay;
+}
+
+- (BOOL)isPlaying
+{
+ return _playing;
+}
+
+- (void)setPlaying:(BOOL)playing
+{
+ if (_playing == playing)
+ return;
+
+ if (playing && !self.isReadyToPlay)
+ return;
+
+ _playing = playing;
+
+ if (_playing)
+ [_audioPlayer play];
+ else
+ [_audioPlayer pause];
+
+ [_audioPlayerDelegate playStateDidChangeForAudioPlayer:self];
+}
+
+- (BOOL)isMuted
+{
+ return _audioPlayer.isMuted;
+}
+
+- (void)setMuted:(BOOL)muted
+{
+ if (self.isMuted == muted)
+ return;
+
+ _audioPlayer.muted = muted;
+ [_audioPlayerDelegate mutedStateDidChangeForAudioPlayer:self];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+
+ if (context == &itemStatusKVOContext) {
+ dispatch_async(dispatch_get_main_queue(), ^{ [_audioPlayerDelegate readyStateDidChangeForAudioPlayer:self]; });
+ return;
+ }
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+@end
</ins></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginEnglishlprojInfoPliststrings"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/English.lproj/InfoPlist.strings (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/English.lproj/InfoPlist.strings         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/English.lproj/InfoPlist.strings        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+\xFF\xFE/* Localized versions of Info.plist keys */
+
+
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginInfoplist"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/Info.plist (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/Info.plist         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/Info.plist        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,36 @@
</span><ins>+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>CFBundleDevelopmentRegion</key>
+        <string>English</string>
+        <key>CFBundleExecutable</key>
+        <string>NetscapeMuteAPIPlugin</string>
+        <key>CFBundleIconFile</key>
+        <string></string>
+        <key>CFBundleIdentifier</key>
+        <string>com.apple.netscapemuteapiplugin</string>
+        <key>CFBundleInfoDictionaryVersion</key>
+        <string>6.0</string>
+        <key>CFBundlePackageType</key>
+        <string>BRPL</string>
+        <key>CFBundleShortVersionString</key>
+        <string>1.0</string>
+        <key>CFBundleSignature</key>
+        <string>????</string>
+        <key>CFBundleVersion</key>
+        <string>1.0</string>
+        <key>WebPluginDescription</key>
+        <string>Simple Netscape plug-in that uses the Mute API</string>
+        <key>WebPluginMIMETypes</key>
+        <dict>
+                <key>test/x-netscape-mute-api-plugin</key>
+                <dict>
+                        <key>WebPluginTypeDescription</key>
+                        <string>Netscape Cocoa Plug-in</string>
+                </dict>
+        </dict>
+        <key>WebPluginName</key>
+        <string>Netscape Mute API Plug-in</string>
+</dict>
+</plist>
</ins></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginMenuHandlerh"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.h (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.h         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.h        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class AudioPlayer;
+
+@interface MenuHandler : NSObject {
+ NSMenu *_menu;
+ AudioPlayer *_audioPlayer;
+}
+
+- (id)initWithAudioPlayer:(AudioPlayer *)audioPlayer;
+- (NSMenu *)menu;
+
+@end
</ins></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginMenuHandlerm"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.m (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.m         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/MenuHandler.m        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,100 @@
</span><ins>+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "MenuHandler.h"
+
+#import "AudioPlayer.h"
+
+@implementation MenuHandler
+
+- (void)_play:(id)sender
+{
+ _audioPlayer.playing = YES;
+}
+
+- (void)_pause:(id)sender
+{
+ _audioPlayer.playing = NO;
+}
+
+- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
+{
+ SEL sel = [anItem action];
+
+ if (sel == @selector(_play:))
+ return _audioPlayer.isReadyToPlay && !_audioPlayer.isPlaying;
+
+ if (sel == @selector(_pause:))
+ return _audioPlayer.isPlaying;
+
+ return NO;
+}
+
+- (id)initWithAudioPlayer:(AudioPlayer *)audioPlayer
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _audioPlayer = [audioPlayer retain];
+
+ // Create the menu
+ _menu = [[NSMenu alloc] initWithTitle:@"Menu"];
+
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"Play" action:@selector(_play:) keyEquivalent:@""];
+ [item setTarget:self];
+ [_menu addItem:item];
+ [item release];
+
+ item = [[NSMenuItem alloc] initWithTitle:@"Pause" action:@selector(_pause:) keyEquivalent:@""];
+ [item setTarget:self];
+ [_menu addItem:item];
+ [item release];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_menu release];
+ [_audioPlayer release];
+
+ [super dealloc];
+}
+
+- (NSMenu *)menu
+{
+ return _menu;
+}
+
+@end
</ins></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginNetscapeMuteAPIPluginxcodeprojprojectpbxproj"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/NetscapeMuteAPIPlugin.xcodeproj/project.pbxproj (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/NetscapeMuteAPIPlugin.xcodeproj/project.pbxproj         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/NetscapeMuteAPIPlugin.xcodeproj/project.pbxproj        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,263 @@
</span><ins>+// !$*UTF8*$!
+{
+        archiveVersion = 1;
+        classes = {
+        };
+        objectVersion = 46;
+        objects = {
+
+/* Begin PBXBuildFile section */
+                1A624E590DCBC7F6006898C8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A624E580DCBC7F6006898C8 /* main.m */; };
+                1A6250420DCBC81B006898C8 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A6250410DCBC81B006898C8 /* Cocoa.framework */; };
+                1AAD6A090DD0DC52003985DA /* MenuHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AAD6A080DD0DC52003985DA /* MenuHandler.m */; };
+                52E2CAF119F856E9001EEB4F /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52E2CAF019F856E9001EEB4F /* AVFoundation.framework */; };
+                52E2CAF419F85A47001EEB4F /* AudioPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 52E2CAF319F85A47001EEB4F /* AudioPlayer.mm */; };
+                8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8D5B49A704867FD3000E48DA /* InfoPlist.strings */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+                089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+                1A624E580DCBC7F6006898C8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+                1A6250410DCBC81B006898C8 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+                1AAD6A070DD0DC52003985DA /* MenuHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuHandler.h; sourceTree = "<group>"; };
+                1AAD6A080DD0DC52003985DA /* MenuHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuHandler.m; sourceTree = "<group>"; };
+                52E2CAF019F856E9001EEB4F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = ../../../../../../../../System/Library/Frameworks/AVFoundation.framework; sourceTree = "<group>"; };
+                52E2CAF219F85A47001EEB4F /* AudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioPlayer.h; sourceTree = "<group>"; };
+                52E2CAF319F85A47001EEB4F /* AudioPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioPlayer.mm; sourceTree = "<group>"; };
+                8D576316048677EA00EA77CD /* NetscapeMuteAPIPlugin.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NetscapeMuteAPIPlugin.plugin; sourceTree = BUILT_PRODUCTS_DIR; };
+                8D576317048677EA00EA77CD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+                8D576313048677EA00EA77CD /* Frameworks */ = {
+                        isa = PBXFrameworksBuildPhase;
+                        buildActionMask = 2147483647;
+                        files = (
+                                52E2CAF119F856E9001EEB4F /* AVFoundation.framework in Frameworks */,
+                                1A6250420DCBC81B006898C8 /* Cocoa.framework in Frameworks */,
+                        );
+                        runOnlyForDeploymentPostprocessing = 0;
+                };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+                089C166AFE841209C02AAC07 /* NetscapeMuteAPIPlugin */ = {
+                        isa = PBXGroup;
+                        children = (
+                                52E2CAF019F856E9001EEB4F /* AVFoundation.framework */,
+                                08FB77AFFE84173DC02AAC07 /* Source */,
+                                089C167CFE841241C02AAC07 /* Resources */,
+                                089C1671FE841209C02AAC07 /* External Frameworks and Libraries */,
+                                19C28FB6FE9D52B211CA2CBB /* Products */,
+                        );
+                        name = NetscapeMuteAPIPlugin;
+                        sourceTree = "<group>";
+                };
+                089C1671FE841209C02AAC07 /* External Frameworks and Libraries */ = {
+                        isa = PBXGroup;
+                        children = (
+                                1A6250410DCBC81B006898C8 /* Cocoa.framework */,
+                        );
+                        name = "External Frameworks and Libraries";
+                        sourceTree = "<group>";
+                };
+                089C167CFE841241C02AAC07 /* Resources */ = {
+                        isa = PBXGroup;
+                        children = (
+                                8D576317048677EA00EA77CD /* Info.plist */,
+                                8D5B49A704867FD3000E48DA /* InfoPlist.strings */,
+                        );
+                        name = Resources;
+                        sourceTree = "<group>";
+                };
+                08FB77AFFE84173DC02AAC07 /* Source */ = {
+                        isa = PBXGroup;
+                        children = (
+                                1A624E580DCBC7F6006898C8 /* main.m */,
+                                1AAD6A070DD0DC52003985DA /* MenuHandler.h */,
+                                1AAD6A080DD0DC52003985DA /* MenuHandler.m */,
+                                52E2CAF219F85A47001EEB4F /* AudioPlayer.h */,
+                                52E2CAF319F85A47001EEB4F /* AudioPlayer.mm */,
+                        );
+                        name = Source;
+                        sourceTree = "<group>";
+                };
+                19C28FB6FE9D52B211CA2CBB /* Products */ = {
+                        isa = PBXGroup;
+                        children = (
+                                8D576316048677EA00EA77CD /* NetscapeMuteAPIPlugin.plugin */,
+                        );
+                        name = Products;
+                        sourceTree = "<group>";
+                };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+                8D57630D048677EA00EA77CD /* NetscapeMuteAPIPlugin */ = {
+                        isa = PBXNativeTarget;
+                        buildConfigurationList = 149C298308902C53008A9EFC /* Build configuration list for PBXNativeTarget "NetscapeMuteAPIPlugin" */;
+                        buildPhases = (
+                                8D576311048677EA00EA77CD /* Sources */,
+                                8D57630F048677EA00EA77CD /* Resources */,
+                                8D576313048677EA00EA77CD /* Frameworks */,
+                        );
+                        buildRules = (
+                        );
+                        dependencies = (
+                        );
+                        name = NetscapeMuteAPIPlugin;
+                        productInstallPath = "$(HOME)/Library/Bundles";
+                        productName = NetscapeMuteAPIPlugin;
+                        productReference = 8D576316048677EA00EA77CD /* NetscapeMuteAPIPlugin.plugin */;
+                        productType = "com.apple.product-type.bundle";
+                };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+                089C1669FE841209C02AAC07 /* Project object */ = {
+                        isa = PBXProject;
+                        attributes = {
+                                LastUpgradeCheck = 0600;
+                        };
+                        buildConfigurationList = 149C298708902C53008A9EFC /* Build configuration list for PBXProject "NetscapeMuteAPIPlugin" */;
+                        compatibilityVersion = "Xcode 3.2";
+                        developmentRegion = English;
+                        hasScannedForEncodings = 1;
+                        knownRegions = (
+                                English,
+                                Japanese,
+                                French,
+                                German,
+                        );
+                        mainGroup = 089C166AFE841209C02AAC07 /* NetscapeMuteAPIPlugin */;
+                        projectDirPath = "";
+                        projectRoot = "";
+                        targets = (
+                                8D57630D048677EA00EA77CD /* NetscapeMuteAPIPlugin */,
+                        );
+                };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+                8D57630F048677EA00EA77CD /* Resources */ = {
+                        isa = PBXResourcesBuildPhase;
+                        buildActionMask = 2147483647;
+                        files = (
+                                8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */,
+                        );
+                        runOnlyForDeploymentPostprocessing = 0;
+                };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+                8D576311048677EA00EA77CD /* Sources */ = {
+                        isa = PBXSourcesBuildPhase;
+                        buildActionMask = 2147483647;
+                        files = (
+                                1A624E590DCBC7F6006898C8 /* main.m in Sources */,
+                                52E2CAF419F85A47001EEB4F /* AudioPlayer.mm in Sources */,
+                                1AAD6A090DD0DC52003985DA /* MenuHandler.m in Sources */,
+                        );
+                        runOnlyForDeploymentPostprocessing = 0;
+                };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+                8D5B49A704867FD3000E48DA /* InfoPlist.strings */ = {
+                        isa = PBXVariantGroup;
+                        children = (
+                                089C167EFE841241C02AAC07 /* English */,
+                        );
+                        name = InfoPlist.strings;
+                        sourceTree = "<group>";
+                };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+                149C298408902C53008A9EFC /* Debug */ = {
+                        isa = XCBuildConfiguration;
+                        buildSettings = {
+                                COMBINE_HIDPI_IMAGES = YES;
+                                COPY_PHASE_STRIP = NO;
+                                FRAMEWORK_SEARCH_PATHS = (
+                                        "$(inherited)",
+                                        "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+                                );
+                                GCC_DEBUGGING_SYMBOLS = full;
+                                GCC_DYNAMIC_NO_PIC = NO;
+                                GCC_ENABLE_FIX_AND_CONTINUE = YES;
+                                GCC_MODEL_TUNING = G5;
+                                GCC_OPTIMIZATION_LEVEL = 0;
+                                INFOPLIST_FILE = Info.plist;
+                                INSTALL_PATH = "$(HOME)/Library/Bundles";
+                                PRODUCT_NAME = NetscapeMuteAPIPlugin;
+                                WRAPPER_EXTENSION = plugin;
+                                ZERO_LINK = YES;
+                        };
+                        name = Debug;
+                };
+                149C298508902C53008A9EFC /* Release */ = {
+                        isa = XCBuildConfiguration;
+                        buildSettings = {
+                                COMBINE_HIDPI_IMAGES = YES;
+                                FRAMEWORK_SEARCH_PATHS = (
+                                        "$(inherited)",
+                                        "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+                                );
+                                GCC_DEBUGGING_SYMBOLS = full;
+                                GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+                                GCC_MODEL_TUNING = G5;
+                                GCC_OPTIMIZATION_LEVEL = 0;
+                                INFOPLIST_FILE = Info.plist;
+                                INSTALL_PATH = "$(HOME)/Library/Bundles";
+                                PRODUCT_NAME = NetscapeMuteAPIPlugin;
+                                WRAPPER_EXTENSION = plugin;
+                        };
+                        name = Release;
+                };
+                149C298808902C53008A9EFC /* Debug */ = {
+                        isa = XCBuildConfiguration;
+                        buildSettings = {
+                                ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "ppc i386 ppc64 x86_64";
+                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                                GCC_WARN_UNUSED_VARIABLE = YES;
+                                ONLY_ACTIVE_ARCH = YES;
+                                PREBINDING = NO;
+                        };
+                        name = Debug;
+                };
+                149C298908902C53008A9EFC /* Release */ = {
+                        isa = XCBuildConfiguration;
+                        buildSettings = {
+                                ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "ppc i386 ppc64 x86_64";
+                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                                GCC_WARN_UNUSED_VARIABLE = YES;
+                                PREBINDING = NO;
+                        };
+                        name = Release;
+                };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+                149C298308902C53008A9EFC /* Build configuration list for PBXNativeTarget "NetscapeMuteAPIPlugin" */ = {
+                        isa = XCConfigurationList;
+                        buildConfigurations = (
+                                149C298408902C53008A9EFC /* Debug */,
+                                149C298508902C53008A9EFC /* Release */,
+                        );
+                        defaultConfigurationIsVisible = 0;
+                        defaultConfigurationName = Release;
+                };
+                149C298708902C53008A9EFC /* Build configuration list for PBXProject "NetscapeMuteAPIPlugin" */ = {
+                        isa = XCConfigurationList;
+                        buildConfigurations = (
+                                149C298808902C53008A9EFC /* Debug */,
+                                149C298908902C53008A9EFC /* Release */,
+                        );
+                        defaultConfigurationIsVisible = 0;
+                        defaultConfigurationName = Release;
+                };
+/* End XCConfigurationList section */
+        };
+        rootObject = 089C1669FE841209C02AAC07 /* Project object */;
+}
</ins></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPluginmainm"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/main.m (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/main.m         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/main.m        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,408 @@
</span><ins>+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "AudioPlayer.h"
+#import "MenuHandler.h"
+#import <Cocoa/Cocoa.h>
+#import <WebKit/npapi.h>
+#import <WebKit/npfunctions.h>
+#import <WebKit/npruntime.h>
+
+// Browser function table
+static NPNetscapeFuncs* browser;
+
+// Structure for per-instance storage
+@interface PluginObject : NSObject <AudioPlayerDelegate> {
+ AudioPlayer *_audioPlayer;
+ MenuHandler *_menuHandler;
+}
+
+@property (nonatomic, readonly) NPP npp;
+@property (nonatomic) NPWindow window;
+@property (nonatomic, strong, readonly) NSURL *audioURL;
+@property (nonatomic, copy, readonly) NSString *displayString;
+@property (nonatomic, strong, readonly) MenuHandler *menuHandler;
+@property (nonatomic) BOOL hasFocus;
+@property (nonatomic, readonly, getter=isPlayingAudio) BOOL playingAudio;
+@property (nonatomic, getter=isMuted) BOOL muted;
+
+- (instancetype)initWithNPP:(NPP)npp audioURL:(NSURL *)audioURL;
+
+@end
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData*);
+NPError NPP_Destroy(NPP instance, NPSavedData**);
+NPError NPP_SetWindow(NPP instance, NPWindow*);
+NPError NPP_NewStream(NPP instance, NPMIMEType, NPStream*, NPBool seekable, uint16_t* stype);
+NPError NPP_DestroyStream(NPP instance, NPStream*, NPReason);
+int32_t NPP_WriteReady(NPP instance, NPStream*);
+int32_t NPP_Write(NPP instance, NPStream*, int32_t offset, int32_t len, void* buffer);
+void NPP_StreamAsFile(NPP instance, NPStream*, const char* fname);
+void NPP_Print(NPP instance, NPPrint* platformPrint);
+int16_t NPP_HandleEvent(NPP instance, void* event);
+void NPP_URLNotify(NPP instance, const char* URL, NPReason, void* notifyData);
+NPError NPP_GetValue(NPP instance, NPPVariable, void *value);
+NPError NPP_SetValue(NPP instance, NPNVariable, void *value);
+
+#pragma export on
+// Mach-o entry points
+NPError NP_Initialize(NPNetscapeFuncs *browserFuncs);
+NPError NP_GetEntryPoints(NPPluginFuncs *);
+void NP_Shutdown(void);
+#pragma export off
+
+static void invalidatePlugin(PluginObject *);
+
+@implementation PluginObject
+
+- (instancetype)initWithNPP:(NPP)npp audioURL:(NSURL *)audioURL
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _npp = npp;
+ _audioURL = [audioURL retain];
+ _audioPlayer = [[AudioPlayer alloc] initWithURL:audioURL];
+ _audioPlayer.audioPlayerDelegate = self;
+
+ NPBool isMuted;
+ if (browser->getvalue(npp, NPNVmuteAudioBool, &isMuted) != NPERR_NO_ERROR)
+ isMuted = FALSE;
+ self.muted = isMuted;
+
+ [self _invalidateDisplayString];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_audioURL release];
+ [_menuHandler release];
+ [_audioPlayer release];
+ [_displayString release];
+
+ [super dealloc];
+}
+
+- (MenuHandler *)menuHandler
+{
+ if (!_menuHandler)
+ _menuHandler = [[MenuHandler alloc] initWithAudioPlayer:_audioPlayer];
+
+ return _menuHandler;
+}
+
+- (BOOL)isPlayingAudio
+{
+ return _audioPlayer.isPlaying;
+}
+
+- (BOOL)isMuted
+{
+ return _audioPlayer.isMuted;
+}
+
+- (void)setMuted:(BOOL)muted
+{
+ _audioPlayer.muted = muted;
+}
+
+- (void)readyStateDidChangeForAudioPlayer:(AudioPlayer *)audioPlayer
+{
+ [self _invalidateDisplayString];
+ invalidatePlugin(self);
+}
+
+- (void)mutedStateDidChangeForAudioPlayer:(AudioPlayer *)audioPlayer
+{
+}
+
+- (void)playStateDidChangeForAudioPlayer:(AudioPlayer *)audioPlayer
+{
+ [self _invalidateDisplayString];
+ invalidatePlugin(self);
+ browser->setvalue(_npp, NPPVpluginIsPlayingAudio, _audioPlayer.isPlaying);
+}
+
+- (void)_invalidateDisplayString
+{
+ [_displayString release];
+
+ NSString *prefix;
+ if (!_audioPlayer.isReadyToPlay)
+ prefix = @"Loading: ";
+ else if (_audioPlayer.isPlaying)
+ prefix = @"Playing: ";
+ else
+ prefix = @"Ready to play: ";
+
+ _displayString = [[NSString stringWithFormat:@"%@ %@", prefix, _audioURL.absoluteString] retain];
+}
+
+@end
+
+
+NPError NP_Initialize(NPNetscapeFuncs* browserFuncs)
+{
+ browser = browserFuncs;
+ return NPERR_NO_ERROR;
+}
+
+NPError NP_GetEntryPoints(NPPluginFuncs* pluginFuncs)
+{
+ pluginFuncs->version = 11;
+ pluginFuncs->size = sizeof(pluginFuncs);
+ pluginFuncs->newp = NPP_New;
+ pluginFuncs->destroy = NPP_Destroy;
+ pluginFuncs->setwindow = NPP_SetWindow;
+ pluginFuncs->newstream = NPP_NewStream;
+ pluginFuncs->destroystream = NPP_DestroyStream;
+ pluginFuncs->asfile = NPP_StreamAsFile;
+ pluginFuncs->writeready = NPP_WriteReady;
+ pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
+ pluginFuncs->print = NPP_Print;
+ pluginFuncs->event = NPP_HandleEvent;
+ pluginFuncs->urlnotify = NPP_URLNotify;
+ pluginFuncs->getvalue = NPP_GetValue;
+ pluginFuncs->setvalue = NPP_SetValue;
+
+ return NPERR_NO_ERROR;
+}
+
+void NP_Shutdown(void)
+{
+
+}
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved)
+{
+ // Create per-instance storage
+ // Ask the browser if it supports the CoreGraphics drawing model
+ NPBool supportsCoreGraphics;
+ if (browser->getvalue(instance, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) != NPERR_NO_ERROR)
+ supportsCoreGraphics = FALSE;
+
+ if (!supportsCoreGraphics)
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+ // If the browser supports the CoreGraphics drawing model, enable it.
+ browser->setvalue(instance, NPPVpluginDrawingModel, (void *)NPDrawingModelCoreGraphics);
+
+ // If the browser supports the Cocoa event model, enable it.
+ NPBool supportsCocoa;
+ if (browser->getvalue(instance, NPNVsupportsCocoaBool, &supportsCocoa) != NPERR_NO_ERROR)
+ supportsCocoa = FALSE;
+
+ if (!supportsCocoa)
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+ browser->setvalue(instance, NPPVpluginEventModel, (void *)NPEventModelCocoa);
+
+ NSURL *audioURL = nil;
+ for (int16_t i = 0; i < argc; i++) {
+ if (!strcasecmp(argn[i], "audiourl")) {
+ NSString *urlString = [NSString stringWithUTF8String:argv[i]];
+ if (urlString)
+ audioURL = [NSURL URLWithString:urlString];
+ break;
+ }
+ }
+
+ PluginObject *obj = [[PluginObject alloc] initWithNPP:instance audioURL:audioURL];
+ instance->pdata = obj;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData** save)
+{
+ // Free per-instance storage
+ PluginObject *obj = instance->pdata;
+ [obj release];
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow* window)
+{
+ PluginObject *obj = instance->pdata;
+ obj.window = *window;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
+{
+ *stype = NP_ASFILEONLY;
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
+{
+ return NPERR_NO_ERROR;
+}
+
+int32_t NPP_WriteReady(NPP instance, NPStream* stream)
+{
+ return 0;
+}
+
+int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer)
+{
+ return 0;
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+}
+
+void NPP_Print(NPP instance, NPPrint* platformPrint)
+{
+}
+
+static void handleDraw(PluginObject *obj, NPCocoaEvent *event)
+{
+ NSGraphicsContext *oldContext = [[NSGraphicsContext currentContext] retain];
+
+ NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:event->data.draw.context flipped:YES];
+
+ [NSGraphicsContext setCurrentContext:context];
+
+ NSRect rect = NSMakeRect(0, 0, obj.window.width, obj.window.height);
+
+ [[NSColor lightGrayColor] set];
+ [NSBezierPath fillRect:rect];
+
+ // If the plugin has focus, draw a focus indicator
+ if (obj.hasFocus) {
+ [[NSColor blackColor] set];
+ NSBezierPath *path = [NSBezierPath bezierPathWithRect:rect];
+ [path setLineWidth:5];
+ [path stroke];
+ }
+
+ [obj.displayString drawAtPoint:NSMakePoint(10, 10) withAttributes:nil];
+
+ [NSGraphicsContext setCurrentContext:oldContext];
+ [oldContext release];
+}
+
+static void invalidatePlugin(PluginObject *obj)
+{
+ NPRect rect;
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = obj.window.width;
+ rect.bottom = obj.window.height;
+
+ browser->invalidaterect(obj.npp, &rect);
+}
+
+
+static void handleMouseEvent(PluginObject *obj, NPCocoaEvent *event)
+{
+ if (event->data.mouse.buttonNumber == 1)
+ browser->popupcontextmenu(obj.npp, (NPNSMenu *)[obj.menuHandler menu]);
+}
+
+int16_t NPP_HandleEvent(NPP instance, void* event)
+{
+ PluginObject *obj = instance->pdata;
+
+ NPCocoaEvent *cocoaEvent = event;
+
+ switch (cocoaEvent->type) {
+ case NPCocoaEventFocusChanged:
+ obj.hasFocus = cocoaEvent->data.focus.hasFocus;
+ invalidatePlugin(obj);
+ return 1;
+
+ case NPCocoaEventDrawRect:
+ handleDraw(obj, cocoaEvent);
+ return 1;
+
+ case NPCocoaEventMouseDown:
+ case NPCocoaEventMouseUp:
+
+ // FIXME: NPCocoaEventMouseMoved is currently disabled in order to see other events more clearly
+ // without "drowning" in mouse moved events.
+// case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseEntered:
+ case NPCocoaEventMouseExited:
+ case NPCocoaEventMouseDragged:
+ case NPCocoaEventScrollWheel:
+ handleMouseEvent(obj, cocoaEvent);
+ return 1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+ PluginObject *obj = instance->pdata;
+
+ switch (variable) {
+ case NPPVpluginIsPlayingAudio:
+ if (value)
+ *((NPBool **)value) = obj.isPlayingAudio;
+ return NPERR_NO_ERROR;
+
+ default:
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+ PluginObject *obj = instance->pdata;
+
+ switch (variable) {
+ case NPNVmuteAudioBool:
+ if (value)
+ obj.muted = *((NPBool *)value);
+ return NPERR_NO_ERROR;
+
+ default:
+ return NPERR_GENERIC_ERROR;
+ }
+}
</ins></span></pre></div>
<a id="trunkExamplesNetscapeMuteAPIPlugintesthtml"></a>
<div class="addfile"><h4>Added: trunk/Examples/NetscapeMuteAPIPlugin/test.html (0 => 176785)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Examples/NetscapeMuteAPIPlugin/test.html         (rev 0)
+++ trunk/Examples/NetscapeMuteAPIPlugin/test.html        2014-12-04 06:42:38 UTC (rev 176785)
</span><span class="lines">@@ -0,0 +1,12 @@
</span><ins>+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html>
+<head>
+<title>Cocoa Mute API Plug-In</title>
+</head>
+<body>
+<p>To test, set audiourl to an absolute URL to an audio file (e.g. mp3).</p>
+<div>
+ <embed width="700" height="50" type="test/x-netscape-mute-api-plugin" audiourl=""></embed>
+</div>
+</body>
+</html>
</ins></span></pre>
</div>
</div>
</body>
</html>