LCOV - code coverage report
Current view: top level - widget/gtk - nsSound.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 219 0.5 %
Date: 2017-07-14 16:53:18 Functions: 0 21 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim:expandtab:shiftwidth=4:tabstop=4:
       3             :  */
       4             : /* This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include <string.h>
       9             : 
      10             : #include "nscore.h"
      11             : #include "plstr.h"
      12             : #include "prlink.h"
      13             : 
      14             : #include "nsSound.h"
      15             : 
      16             : #include "HeadlessSound.h"
      17             : #include "nsIURL.h"
      18             : #include "nsIFileURL.h"
      19             : #include "nsNetUtil.h"
      20             : #include "nsIChannel.h"
      21             : #include "nsCOMPtr.h"
      22             : #include "nsString.h"
      23             : #include "nsDirectoryService.h"
      24             : #include "nsDirectoryServiceDefs.h"
      25             : #include "mozilla/FileUtils.h"
      26             : #include "mozilla/Services.h"
      27             : #include "mozilla/Unused.h"
      28             : #include "nsIStringBundle.h"
      29             : #include "nsIXULAppInfo.h"
      30             : #include "nsContentUtils.h"
      31             : #include "gfxPlatform.h"
      32             : #include "mozilla/ClearOnShutdown.h"
      33             : 
      34             : #include <stdio.h>
      35             : #include <unistd.h>
      36             : 
      37             : #include <gtk/gtk.h>
      38             : static PRLibrary *libcanberra = nullptr;
      39             : 
      40             : /* used to play sounds with libcanberra. */
      41             : typedef struct _ca_context ca_context;
      42             : typedef struct _ca_proplist ca_proplist;
      43             : 
      44             : typedef void (*ca_finish_callback_t) (ca_context *c,
      45             :                                       uint32_t id,
      46             :                                       int error_code,
      47             :                                       void *userdata);
      48             : 
      49             : typedef int (*ca_context_create_fn) (ca_context **);
      50             : typedef int (*ca_context_destroy_fn) (ca_context *);
      51             : typedef int (*ca_context_play_fn) (ca_context *c,
      52             :                                    uint32_t id,
      53             :                                    ...);
      54             : typedef int (*ca_context_change_props_fn) (ca_context *c,
      55             :                                            ...);
      56             : typedef int (*ca_proplist_create_fn) (ca_proplist **);
      57             : typedef int (*ca_proplist_destroy_fn) (ca_proplist *);
      58             : typedef int (*ca_proplist_sets_fn) (ca_proplist *c,
      59             :                                     const char *key,
      60             :                                     const char *value);
      61             : typedef int (*ca_context_play_full_fn) (ca_context *c,
      62             :                                         uint32_t id,
      63             :                                         ca_proplist *p,
      64             :                                         ca_finish_callback_t cb,
      65             :                                         void *userdata);
      66             : 
      67             : static ca_context_create_fn ca_context_create;
      68             : static ca_context_destroy_fn ca_context_destroy;
      69             : static ca_context_play_fn ca_context_play;
      70             : static ca_context_change_props_fn ca_context_change_props;
      71             : static ca_proplist_create_fn ca_proplist_create;
      72             : static ca_proplist_destroy_fn ca_proplist_destroy;
      73             : static ca_proplist_sets_fn ca_proplist_sets;
      74             : static ca_context_play_full_fn ca_context_play_full;
      75             : 
      76             : struct ScopedCanberraFile {
      77           0 :     explicit ScopedCanberraFile(nsIFile *file): mFile(file) {};
      78             : 
      79           0 :     ~ScopedCanberraFile() {
      80           0 :         if (mFile) {
      81           0 :             mFile->Remove(false);
      82             :         }
      83           0 :     }
      84             : 
      85           0 :     void forget() {
      86           0 :         mozilla::Unused << mFile.forget();
      87           0 :     }
      88           0 :     nsIFile* operator->() { return mFile; }
      89           0 :     operator nsIFile*() { return mFile; }
      90             : 
      91             :     nsCOMPtr<nsIFile> mFile;
      92             : };
      93             : 
      94             : static ca_context*
      95           0 : ca_context_get_default()
      96             : {
      97             :     // This allows us to avoid race conditions with freeing the context by handing that
      98             :     // responsibility to Glib, and still use one context at a time
      99             :     static GStaticPrivate ctx_static_private = G_STATIC_PRIVATE_INIT;
     100             : 
     101           0 :     ca_context* ctx = (ca_context*) g_static_private_get(&ctx_static_private);
     102             : 
     103           0 :     if (ctx) {
     104           0 :         return ctx;
     105             :     }
     106             : 
     107           0 :     ca_context_create(&ctx);
     108           0 :     if (!ctx) {
     109           0 :         return nullptr;
     110             :     }
     111             : 
     112           0 :     g_static_private_set(&ctx_static_private, ctx, (GDestroyNotify) ca_context_destroy);
     113             : 
     114           0 :     GtkSettings* settings = gtk_settings_get_default();
     115           0 :     if (g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
     116             :                                      "gtk-sound-theme-name")) {
     117           0 :         gchar* sound_theme_name = nullptr;
     118             :         g_object_get(settings, "gtk-sound-theme-name", &sound_theme_name,
     119           0 :                      nullptr);
     120             : 
     121           0 :         if (sound_theme_name) {
     122             :             ca_context_change_props(ctx, "canberra.xdg-theme.name",
     123           0 :                                     sound_theme_name, nullptr);
     124           0 :             g_free(sound_theme_name);
     125             :         }
     126             :     }
     127             : 
     128             :     nsCOMPtr<nsIStringBundleService> bundleService =
     129           0 :         mozilla::services::GetStringBundleService();
     130           0 :     if (bundleService) {
     131           0 :         nsCOMPtr<nsIStringBundle> brandingBundle;
     132           0 :         bundleService->CreateBundle("chrome://branding/locale/brand.properties",
     133           0 :                                     getter_AddRefs(brandingBundle));
     134           0 :         if (brandingBundle) {
     135           0 :             nsAutoString wbrand;
     136           0 :             brandingBundle->GetStringFromName(u"brandShortName",
     137           0 :                                               getter_Copies(wbrand));
     138           0 :             NS_ConvertUTF16toUTF8 brand(wbrand);
     139             : 
     140           0 :             ca_context_change_props(ctx, "application.name", brand.get(),
     141           0 :                                     nullptr);
     142             :         }
     143             :     }
     144             : 
     145           0 :     nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
     146           0 :     if (appInfo) {
     147           0 :         nsAutoCString version;
     148           0 :         appInfo->GetVersion(version);
     149             : 
     150           0 :         ca_context_change_props(ctx, "application.version", version.get(),
     151           0 :                                 nullptr);
     152             :     }
     153             : 
     154             :     ca_context_change_props(ctx, "application.icon_name", MOZ_APP_NAME,
     155           0 :                             nullptr);
     156             : 
     157           0 :     return ctx;
     158             : }
     159             : 
     160             : static void
     161           0 : ca_finish_cb(ca_context *c,
     162             :              uint32_t id,
     163             :              int error_code,
     164             :              void *userdata)
     165             : {
     166           0 :     nsIFile *file = reinterpret_cast<nsIFile *>(userdata);
     167           0 :     if (file) {
     168           0 :         file->Remove(false);
     169           0 :         NS_RELEASE(file);
     170             :     }
     171           0 : }
     172             : 
     173           0 : NS_IMPL_ISUPPORTS(nsSound, nsISound, nsIStreamLoaderObserver)
     174             : 
     175             : ////////////////////////////////////////////////////////////////////////
     176           0 : nsSound::nsSound()
     177             : {
     178           0 :     mInited = false;
     179           0 : }
     180             : 
     181           0 : nsSound::~nsSound()
     182             : {
     183           0 : }
     184             : 
     185             : NS_IMETHODIMP
     186           0 : nsSound::Init()
     187             : {
     188             :     // This function is designed so that no library is compulsory, and
     189             :     // one library missing doesn't cause the other(s) to not be used.
     190           0 :     if (mInited)
     191           0 :         return NS_OK;
     192             : 
     193           0 :     mInited = true;
     194             : 
     195           0 :     if (!libcanberra) {
     196           0 :         libcanberra = PR_LoadLibrary("libcanberra.so.0");
     197           0 :         if (libcanberra) {
     198           0 :             ca_context_create = (ca_context_create_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_create");
     199           0 :             if (!ca_context_create) {
     200           0 :                 PR_UnloadLibrary(libcanberra);
     201           0 :                 libcanberra = nullptr;
     202             :             } else {
     203             :                 // at this point we know we have a good libcanberra library
     204           0 :                 ca_context_destroy = (ca_context_destroy_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_destroy");
     205           0 :                 ca_context_play = (ca_context_play_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_play");
     206           0 :                 ca_context_change_props = (ca_context_change_props_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_change_props");
     207           0 :                 ca_proplist_create = (ca_proplist_create_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_create");
     208           0 :                 ca_proplist_destroy = (ca_proplist_destroy_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_destroy");
     209           0 :                 ca_proplist_sets = (ca_proplist_sets_fn) PR_FindFunctionSymbol(libcanberra, "ca_proplist_sets");
     210           0 :                 ca_context_play_full = (ca_context_play_full_fn) PR_FindFunctionSymbol(libcanberra, "ca_context_play_full");
     211             :             }
     212             :         }
     213             :     }
     214             : 
     215           0 :     return NS_OK;
     216             : }
     217             : 
     218             : /* static */ void
     219           0 : nsSound::Shutdown()
     220             : {
     221           0 :     if (libcanberra) {
     222           0 :         PR_UnloadLibrary(libcanberra);
     223           0 :         libcanberra = nullptr;
     224             :     }
     225           0 : }
     226             : 
     227             : namespace mozilla {
     228             : namespace sound {
     229           3 : StaticRefPtr<nsISound> sInstance;
     230             : }
     231             : }
     232             : /* static */ already_AddRefed<nsISound>
     233           0 : nsSound::GetInstance()
     234             : {
     235             :     using namespace mozilla::sound;
     236             : 
     237           0 :     if (!sInstance) {
     238           0 :         if (gfxPlatform::IsHeadless()) {
     239           0 :             sInstance = new widget::HeadlessSound();
     240             :         } else {
     241           0 :             sInstance = new nsSound();
     242             :         }
     243           0 :         ClearOnShutdown(&sInstance);
     244             :     }
     245             : 
     246           0 :     RefPtr<nsISound> service = sInstance.get();
     247           0 :     return service.forget();
     248             : }
     249             : 
     250           0 : NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
     251             :                                         nsISupports *context,
     252             :                                         nsresult aStatus,
     253             :                                         uint32_t dataLen,
     254             :                                         const uint8_t *data)
     255             : {
     256             :     // print a load error on bad status, and return
     257           0 :     if (NS_FAILED(aStatus)) {
     258             : #ifdef DEBUG
     259           0 :         if (aLoader) {
     260           0 :             nsCOMPtr<nsIRequest> request;
     261           0 :             aLoader->GetRequest(getter_AddRefs(request));
     262           0 :             if (request) {
     263           0 :                 nsCOMPtr<nsIURI> uri;
     264           0 :                 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     265           0 :                 if (channel) {
     266           0 :                     channel->GetURI(getter_AddRefs(uri));
     267           0 :                     if (uri) {
     268           0 :                         printf("Failed to load %s\n",
     269           0 :                                uri->GetSpecOrDefault().get());
     270             :                     }
     271             :                 }
     272             :             }
     273             :         }
     274             : #endif
     275           0 :         return aStatus;
     276             :     }
     277             : 
     278           0 :     nsCOMPtr<nsIFile> tmpFile;
     279           0 :     nsDirectoryService::gService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
     280           0 :                                       getter_AddRefs(tmpFile));
     281             : 
     282           0 :     nsresult rv = tmpFile->AppendNative(nsDependentCString("mozilla_audio_sample"));
     283           0 :     if (NS_FAILED(rv)) {
     284           0 :         return rv;
     285             :     }
     286             : 
     287           0 :     rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, PR_IRUSR | PR_IWUSR);
     288           0 :     if (NS_FAILED(rv)) {
     289           0 :         return rv;
     290             :     }
     291             : 
     292           0 :     ScopedCanberraFile canberraFile(tmpFile);
     293             : 
     294           0 :     mozilla::AutoFDClose fd;
     295           0 :     rv = canberraFile->OpenNSPRFileDesc(PR_WRONLY, PR_IRUSR | PR_IWUSR,
     296           0 :                                         &fd.rwget());
     297           0 :     if (NS_FAILED(rv)) {
     298           0 :         return rv;
     299             :     }
     300             : 
     301             :     // XXX: Should we do this on another thread?
     302           0 :     uint32_t length = dataLen;
     303           0 :     while (length > 0) {
     304           0 :         int32_t amount = PR_Write(fd, data, length);
     305           0 :         if (amount < 0) {
     306           0 :             return NS_ERROR_FAILURE;
     307             :         }
     308           0 :         length -= amount;
     309           0 :         data += amount;
     310             :     }
     311             : 
     312           0 :     ca_context* ctx = ca_context_get_default();
     313           0 :     if (!ctx) {
     314           0 :         return NS_ERROR_OUT_OF_MEMORY;
     315             :     }
     316             : 
     317             :     ca_proplist *p;
     318           0 :     ca_proplist_create(&p);
     319           0 :     if (!p) {
     320           0 :         return NS_ERROR_OUT_OF_MEMORY;
     321             :     }
     322             : 
     323           0 :     nsAutoCString path;
     324           0 :     rv = canberraFile->GetNativePath(path);
     325           0 :     if (NS_FAILED(rv)) {
     326           0 :         return rv;
     327             :     }
     328             : 
     329           0 :     ca_proplist_sets(p, "media.filename", path.get());
     330           0 :     if (ca_context_play_full(ctx, 0, p, ca_finish_cb, canberraFile) >= 0) {
     331             :         // Don't delete the temporary file here if ca_context_play_full succeeds
     332           0 :         canberraFile.forget();
     333             :     }
     334           0 :     ca_proplist_destroy(p);
     335             : 
     336           0 :     return NS_OK;
     337             : }
     338             : 
     339           0 : NS_IMETHODIMP nsSound::Beep()
     340             : {
     341           0 :     ::gdk_beep();
     342           0 :     return NS_OK;
     343             : }
     344             : 
     345           0 : NS_IMETHODIMP nsSound::Play(nsIURL *aURL)
     346             : {
     347           0 :     if (!mInited)
     348           0 :         Init();
     349             : 
     350           0 :     if (!libcanberra)
     351           0 :         return NS_ERROR_NOT_AVAILABLE;
     352             : 
     353             :     bool isFile;
     354           0 :     nsresult rv = aURL->SchemeIs("file", &isFile);
     355           0 :     if (NS_SUCCEEDED(rv) && isFile) {
     356           0 :         ca_context* ctx = ca_context_get_default();
     357           0 :         if (!ctx) {
     358           0 :             return NS_ERROR_OUT_OF_MEMORY;
     359             :         }
     360             : 
     361           0 :         nsAutoCString spec;
     362           0 :         rv = aURL->GetSpec(spec);
     363           0 :         if (NS_FAILED(rv)) {
     364           0 :             return rv;
     365             :         }
     366           0 :         gchar *path = g_filename_from_uri(spec.get(), nullptr, nullptr);
     367           0 :         if (!path) {
     368           0 :             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
     369             :         }
     370             : 
     371           0 :         ca_context_play(ctx, 0, "media.filename", path, nullptr);
     372           0 :         g_free(path);
     373             :     } else {
     374           0 :         nsCOMPtr<nsIStreamLoader> loader;
     375           0 :         rv = NS_NewStreamLoader(getter_AddRefs(loader),
     376             :                                 aURL,
     377             :                                 this, // aObserver
     378             :                                 nsContentUtils::GetSystemPrincipal(),
     379             :                                 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
     380           0 :                                 nsIContentPolicy::TYPE_OTHER);
     381             :     }
     382             : 
     383           0 :     return rv;
     384             : }
     385             : 
     386           0 : NS_IMETHODIMP nsSound::PlayEventSound(uint32_t aEventId)
     387             : {
     388           0 :     if (!mInited)
     389           0 :         Init();
     390             : 
     391           0 :     if (!libcanberra)
     392           0 :         return NS_OK;
     393             : 
     394             :     // Do we even want alert sounds?
     395           0 :     GtkSettings* settings = gtk_settings_get_default();
     396             : 
     397           0 :     if (g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
     398             :                                      "gtk-enable-event-sounds")) {
     399           0 :         gboolean enable_sounds = TRUE;
     400           0 :         g_object_get(settings, "gtk-enable-event-sounds", &enable_sounds, nullptr);
     401             : 
     402           0 :         if (!enable_sounds) {
     403           0 :             return NS_OK;
     404             :         }
     405             :     }
     406             : 
     407           0 :     ca_context* ctx = ca_context_get_default();
     408           0 :     if (!ctx) {
     409           0 :         return NS_ERROR_OUT_OF_MEMORY;
     410             :     }
     411             : 
     412           0 :     switch (aEventId) {
     413             :         case EVENT_ALERT_DIALOG_OPEN:
     414           0 :             ca_context_play(ctx, 0, "event.id", "dialog-warning", nullptr);
     415           0 :             break;
     416             :         case EVENT_CONFIRM_DIALOG_OPEN:
     417           0 :             ca_context_play(ctx, 0, "event.id", "dialog-question", nullptr);
     418           0 :             break;
     419             :         case EVENT_NEW_MAIL_RECEIVED:
     420           0 :             ca_context_play(ctx, 0, "event.id", "message-new-email", nullptr);
     421           0 :             break;
     422             :         case EVENT_MENU_EXECUTE:
     423           0 :             ca_context_play(ctx, 0, "event.id", "menu-click", nullptr);
     424           0 :             break;
     425             :         case EVENT_MENU_POPUP:
     426           0 :             ca_context_play(ctx, 0, "event.id", "menu-popup", nullptr);
     427           0 :             break;
     428             :     }
     429           0 :     return NS_OK;
     430             : }
     431             : 
     432           0 : NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
     433             : {
     434           0 :     if (NS_IsMozAliasSound(aSoundAlias)) {
     435           0 :         NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead");
     436             :         uint32_t eventId;
     437           0 :         if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG))
     438           0 :             eventId = EVENT_ALERT_DIALOG_OPEN;
     439           0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG))
     440           0 :             eventId = EVENT_CONFIRM_DIALOG_OPEN;
     441           0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
     442           0 :             eventId = EVENT_NEW_MAIL_RECEIVED;
     443           0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE))
     444           0 :             eventId = EVENT_MENU_EXECUTE;
     445           0 :         else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP))
     446           0 :             eventId = EVENT_MENU_POPUP;
     447             :         else
     448           0 :             return NS_OK;
     449           0 :         return PlayEventSound(eventId);
     450             :     }
     451             : 
     452             :     nsresult rv;
     453           0 :     nsCOMPtr <nsIURI> fileURI;
     454             : 
     455             :     // create a nsIFile and then a nsIFileURL from that
     456           0 :     nsCOMPtr <nsIFile> soundFile;
     457           0 :     rv = NS_NewLocalFile(aSoundAlias, true,
     458           0 :                          getter_AddRefs(soundFile));
     459           0 :     NS_ENSURE_SUCCESS(rv,rv);
     460             : 
     461           0 :     rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile);
     462           0 :     NS_ENSURE_SUCCESS(rv,rv);
     463             : 
     464           0 :     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI,&rv);
     465           0 :     NS_ENSURE_SUCCESS(rv,rv);
     466             : 
     467           0 :     rv = Play(fileURL);
     468             : 
     469           0 :     return rv;
     470             : }

Generated by: LCOV version 1.13