sbMetadataManager.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 // INCLUDES ===================================================================
26 #include <nscore.h>
27 #include <nspr.h>
28 #include "sbMetadataManager.h"
29 
30 #include <nsAutoLock.h>
31 #include <nsXPCOM.h>
32 #include <nsCOMPtr.h>
33 #include <nsAutoPtr.h>
34 #include <nsMemory.h>
35 #include <nsILocalFile.h>
36 #include <nsServiceManagerUtils.h>
37 #include <nsComponentManagerUtils.h>
38 #include <nsIComponentRegistrar.h>
39 #include <nsISupportsPrimitives.h>
40 
41 #include <nsIURI.h>
42 #include <nsIFileStreams.h>
43 #include <nsIFileURL.h>
44 #include <nsIIOService.h>
45 
46 #include <nsNetUtil.h>
47 
48 #include <nsStringGlue.h>
49 
50 #include "prlog.h"
51 #ifdef PR_LOGGING
52  extern PRLogModuleInfo* gMetadataLog;
53  #define LOG(args) \
54  PR_BEGIN_MACRO \
55  if (!gMetadataLog) \
56  gMetadataLog = PR_NewLogModule("metadata"); \
57  PR_LOG(gMetadataLog, PR_LOG_DEBUG, args); \
58  PR_END_MACRO
59  #ifdef __GNUC__
60  #define __FUNCTION__ __PRETTY_FUNCTION__
61  #endif
62 #else
63  #define LOG(args) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
64 #endif
65 
66 
67 #include <sbLockUtils.h>
68 
69 // DEFINES ====================================================================
70 
71 // FUNCTIONS ==================================================================
72 
73 // GLOBALS ====================================================================
75 
76 // CLASSES ====================================================================
78 
79 //-----------------------------------------------------------------------------
81 : m_pContractListLock(nsnull)
82 {
83  m_pContractListLock = PR_NewLock();
84  NS_ASSERTION(m_pContractListLock, "Failed to create sbMetadataManager::m_pContractListLock! Object *not* threadsafe!");
85 
86  // Find the list of handlers for this object.
87  nsresult rv;
88  nsCOMPtr<nsIComponentRegistrar> registrar;
89  rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
90  if (rv != NS_OK)
91  return;
92 
93  nsCOMPtr<nsISimpleEnumerator> simpleEnumerator;
94  rv = registrar->EnumerateContractIDs(getter_AddRefs(simpleEnumerator));
95  if (rv != NS_OK)
96  return;
97 
98  PRBool moreAvailable = PR_FALSE;
99  while(simpleEnumerator->HasMoreElements(&moreAvailable) == NS_OK && moreAvailable)
100  {
101  nsCOMPtr<nsISupportsCString> contractString;
102  if (simpleEnumerator->GetNext(getter_AddRefs(contractString)) == NS_OK)
103  {
104  nsCString contractID;
105  contractString->GetData(contractID);
106  if (contractID.Find("@songbirdnest.com/Songbird/MetadataHandler/") != -1)
107  {
108  m_ContractList.push_back(contractID);
109  }
110  }
111  }
112 }
113 
114 //-----------------------------------------------------------------------------
115 sbMetadataManager::~sbMetadataManager()
116 {
117  if(m_pContractListLock)
118  {
119  PR_DestroyLock(m_pContractListLock);
120  m_pContractListLock = nsnull;
121  }
122 }
123 
124 //-----------------------------------------------------------------------------
125 sbMetadataManager *sbMetadataManager::GetSingleton()
126 {
127  if (gMetadataManager) {
128  NS_ADDREF(gMetadataManager);
129  return gMetadataManager;
130  }
131 
132  NS_NEWXPCOM(gMetadataManager, sbMetadataManager);
133  if (!gMetadataManager)
134  return nsnull;
135 
136  NS_ADDREF(gMetadataManager);
137  NS_ADDREF(gMetadataManager);
138 
139  return gMetadataManager;
140 }
141 
142 //-----------------------------------------------------------------------------
143 void sbMetadataManager::DestroySingleton()
144 {
145  nsRefPtr<sbMetadataManager> dummy;
146  dummy.swap(gMetadataManager);
147 }
148 
149 nsresult sbMetadataManager::GetHandlerInternal(sbIMetadataHandler *aHandler,
150  const nsAString &strURL,
151  sbIMetadataHandler **_retval)
152 {
153  // TODO Bad times! Shouldn't do anything involving channels off of the main thread.
154  // Need to refactor to use streams instead of channels.
155 
156  sbSimpleAutoLock lock(m_pContractListLock);
157 
158  if(!_retval) return NS_ERROR_NULL_POINTER;
159  *_retval = nsnull;
160  nsresult rv = NS_ERROR_UNEXPECTED;
161 
162  nsRefPtr<sbIMetadataHandler> pHandler;
163  nsCOMPtr<nsIChannel> pChannel;
164  nsCOMPtr<nsIIOService> pIOService = do_GetIOService(&rv);
165  NS_ENSURE_SUCCESS(rv, rv);
166 
167  NS_ConvertUTF16toUTF8 cstrURL(strURL);
168  LOG(("sbMetadataManager:: found new URI \"%s\"\n", cstrURL.get()));
169 
170  nsCOMPtr<nsIURI> pURI;
171  rv = pIOService->NewURI(cstrURL, nsnull, nsnull, getter_AddRefs(pURI));
172  NS_ENSURE_SUCCESS(rv, rv);
173 
174  //
175  // Apparently, somewhere in here, it fails for local mp3 files on linux and mac.
176  //
177 
178  nsCString cstrScheme;
179  rv = pURI->GetScheme(cstrScheme);
180  NS_ENSURE_SUCCESS(rv, rv);
181 
182  if(cstrScheme.Length() <= 1)
183  {
184  nsCString cstrFixedURL = NS_LITERAL_CSTRING("file://");
185  cstrFixedURL += cstrURL;
186 
187  pIOService->NewURI(cstrFixedURL, nsnull, nsnull, getter_AddRefs(pURI));
188  rv = pURI->GetScheme(cstrScheme);
189  NS_ENSURE_SUCCESS(rv, rv);
190  }
191 
192 #if defined(XP_UNIX) && !defined(XP_MACOSX)
193  // If local file and on Linux, attempt to use file persistent descriptor
194  // instead of using the plain old file path.
195  // See bug #6227 and #6341.
196  if(cstrScheme.EqualsLiteral("file")) {
197 
198  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(pURI, &rv);
199  if(NS_SUCCEEDED(rv)) {
200 
201  nsCOMPtr<nsIFile> file;
202  rv = fileURL->GetFile(getter_AddRefs(file));
203  NS_ENSURE_SUCCESS(rv, rv);
204 
205  nsCOMPtr<nsIFile> cloneFile;
206  rv = file->Clone(getter_AddRefs(cloneFile));
207  NS_ENSURE_SUCCESS(rv, rv);
208 
209  nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(cloneFile));
210 
211  if (localFile) {
212  nsCString spec;
213  nsresult rv2 = localFile->GetPersistentDescriptor(spec);
214  nsCOMPtr<nsINetUtil> netUtil =
215  do_CreateInstance("@mozilla.org/network/util;1", &rv2);
216  nsCString escapedSpec;
217  rv2 = netUtil->EscapeString(spec,
218  nsINetUtil::ESCAPE_URL_PATH,
219  escapedSpec);
220  nsCOMPtr<nsIURI> pNewURI;
221  if (NS_SUCCEEDED(rv2)) {
222  escapedSpec.Insert("file://", 0);
223  rv2 = pIOService->NewURI(escapedSpec, nsnull, nsnull, getter_AddRefs(pNewURI));
224  if (NS_SUCCEEDED(rv2)) {
225  LOG(("using persistentDescriptor instead: %s\n", escapedSpec.BeginReading()));
226  pURI = pNewURI;
227  }
228  }
229  }
230  }
231  }
232 #endif
233 
234  rv = pIOService->NewChannelFromURI(pURI, getter_AddRefs(pChannel));
235  NS_ENSURE_SUCCESS(rv, rv);
236 
237  // Local types to ease handling.
238  handlerlist_t handlerlist; // hooray for autosorting (std::set)
239 
240  nsCString u8Url;
241  rv = pURI->GetSpec( u8Url );
242  NS_ENSURE_SUCCESS(rv, rv);
243 
244  nsString url = NS_ConvertUTF8toUTF16(u8Url);
245 
246  NS_ENSURE_TRUE(m_ContractList.size() > 0, NS_ERROR_FAILURE);
247 
248  // Go through the list of contract ids, and make them vote on the url
249  for (contractlist_t::iterator i = m_ContractList.begin(); i != m_ContractList.end(); i++ )
250  {
251  nsCOMPtr<sbIMetadataHandler> handler = do_CreateInstance((*i).get(), &rv);
252  if(NS_SUCCEEDED(rv) && handler.get())
253  {
254  PRInt32 vote;
255  handler->Vote( url, &vote );
256  if (vote >= 0) // If everyone returns -1, give up.
257  {
258  sbMetadataHandlerItem item;
259  item.m_Handler = handler;
260  item.m_Vote = vote;
261  #if PR_LOGGING
262  item.m_ContractID = (*i);
263  #endif
264  handlerlist.insert( item ); // Sorted list (std::set)
265  }
266  }
267  }
268 
269  // If there's anything in the list
270  if ( handlerlist.rbegin() != handlerlist.rend() )
271  {
272  handlerlist_t::reverse_iterator i = handlerlist.rbegin();
273 
274  if (!aHandler)
275  {
276  // The end of the list had the highest vote
277  pHandler = (*i).m_Handler;
278  }
279  else
280  {
281  // If someone passed in a previous handler, attempt to fetch the
282  // next one instead.
283  nsCString previousContractID, nextContractID;
284  // This never fails.
285  aHandler->GetContractID(previousContractID);
286 
287  bool useNext = false;
288  for(; i != handlerlist.rend(); ++i)
289  {
290  if (useNext)
291  {
292  // Here we go, the next handler!
293  pHandler = (*i).m_Handler;
294  break;
295  }
296 
297  (*i).m_Handler->GetContractID(nextContractID);
298  if (nextContractID == previousContractID)
299  {
300  // Found previous handler, use the next one.
301  useNext = true;
302  }
303  }
304  }
305  }
306 
307 #if defined(PR_LOGGING)
308  if (pHandler) {
309  nsCString contractID;
310  pHandler->GetContractID(contractID);
311  LOG(("%s[%p]: using handler %s", __FUNCTION__, this, contractID.BeginReading()));
312  }
313 #endif
314 
315  NS_ENSURE_TRUE(pHandler, NS_ERROR_UNEXPECTED);
316 
317  // So, if we have anything, set it up and send it back.
318  rv = pHandler->SetChannel(pChannel);
319  NS_ENSURE_SUCCESS(rv, rv);
320 
321  pHandler.swap(*_retval);
322 
323  return NS_OK;
324 }
325 
326 //-----------------------------------------------------------------------------
327 /* sbIMetadataHandler GetHandlerForMediaURL (in wstring strURL); */
328 NS_IMETHODIMP sbMetadataManager::GetHandlerForMediaURL(const nsAString &strURL, sbIMetadataHandler **_retval)
329 {
330  nsresult rv = GetHandlerInternal(nsnull, strURL, _retval);
331  NS_ENSURE_SUCCESS(rv, rv);
332 
333  return NS_OK;
334 }
335 
336 //-----------------------------------------------------------------------------
337 /* sbIMetadataHandler GetNextHandlerForMediaURL (in sbIMetdataHandler aHandler, in wstring strURL); */
338 NS_IMETHODIMP sbMetadataManager::GetNextHandlerForMediaURL(
339  sbIMetadataHandler *aHandler,
340  const nsAString &aUrl,
341  sbIMetadataHandler **_retval)
342 {
343  NS_ENSURE_ARG_POINTER(aHandler);
344  NS_ENSURE_ARG_POINTER(_retval);
345  nsresult rv = GetHandlerInternal(aHandler, aUrl, _retval);
346  NS_ENSURE_SUCCESS(rv, rv);
347 
348  return NS_OK;
349 }
return NS_OK
var registrar
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
The manager from which to request a metadata handler.
NS_IMPL_THREADSAFE_ISUPPORTS1(sbDeviceCapsCompatibility, sbIDeviceCapsCompatibility) sbDeviceCapsCompatibility
An object capable of manipulating the metadata tags for a media file.
#define LOG(args)
function url(spec)
sbMetadataManager * gMetadataManager
_getSelectedPageStyle s i
GstMessage gpointer data sbGStreamerMessageHandler * handler
var file