widget/windows/winrt/nsMetroFilePicker.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/windows/winrt/nsMetroFilePicker.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,484 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsMetroFilePicker.h"
    1.10 +#include "nsComponentManagerUtils.h"
    1.11 +#include "nsNetUtil.h"
    1.12 +#include "nsAutoPtr.h"
    1.13 +#include "MetroUtils.h"
    1.14 +
    1.15 +#include <windows.ui.viewmanagement.h>
    1.16 +#include <windows.storage.search.h>
    1.17 +
    1.18 +using namespace ABI::Windows::Foundation;
    1.19 +using namespace ABI::Windows::Foundation::Collections;
    1.20 +using namespace ABI::Windows::Storage;
    1.21 +using namespace ABI::Windows::Storage::Pickers;
    1.22 +using namespace ABI::Windows::Storage::Streams;
    1.23 +using namespace ABI::Windows::UI;
    1.24 +using namespace ABI::Windows::UI::ViewManagement;
    1.25 +using namespace Microsoft::WRL;
    1.26 +using namespace Microsoft::WRL::Wrappers;
    1.27 +using namespace mozilla::widget::winrt;
    1.28 +
    1.29 +///////////////////////////////////////////////////////////////////////////////
    1.30 +// nsIFilePicker
    1.31 +
    1.32 +nsMetroFilePicker::nsMetroFilePicker()
    1.33 +{
    1.34 +}
    1.35 +
    1.36 +nsMetroFilePicker::~nsMetroFilePicker()
    1.37 +{
    1.38 +}
    1.39 +
    1.40 +NS_IMPL_ISUPPORTS(nsMetroFilePicker, nsIFilePicker)
    1.41 +
    1.42 +NS_IMETHODIMP
    1.43 +nsMetroFilePicker::Init(nsIDOMWindow *parent, const nsAString& title, int16_t mode)
    1.44 +{
    1.45 +  mMode = mode;
    1.46 +  HRESULT hr;
    1.47 +  switch(mMode) {
    1.48 +  case nsIFilePicker::modeOpen:
    1.49 +  case nsIFilePicker::modeOpenMultiple:
    1.50 +    hr = ActivateGenericInstance(RuntimeClass_Windows_Storage_Pickers_FileOpenPicker, mFileOpenPicker);
    1.51 +    AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
    1.52 +    return NS_OK;
    1.53 +  case nsIFilePicker::modeSave:
    1.54 +    hr = ActivateGenericInstance(RuntimeClass_Windows_Storage_Pickers_FileSavePicker, mFileSavePicker);
    1.55 +    AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
    1.56 +    return NS_OK;
    1.57 +  default:
    1.58 +    return NS_ERROR_NOT_IMPLEMENTED;
    1.59 +  }
    1.60 +}
    1.61 +
    1.62 +NS_IMETHODIMP
    1.63 +nsMetroFilePicker::Show(int16_t *aReturnVal)
    1.64 +{
    1.65 +  // Metro file picker only offers an async variant which calls back to the 
    1.66 +  // UI thread, which is the main thread.  We therefore can't call it
    1.67 +  // synchronously from the main thread.
    1.68 +  return NS_ERROR_NOT_IMPLEMENTED;
    1.69 +}
    1.70 +
    1.71 +HRESULT nsMetroFilePicker::OnPickSingleFile(IAsyncOperation<StorageFile*>* aFile,
    1.72 +                                            AsyncStatus aStatus)
    1.73 +{
    1.74 +  if (aStatus != ABI::Windows::Foundation::AsyncStatus::Completed) {
    1.75 +    if (mCallback)
    1.76 +      mCallback->Done(nsIFilePicker::returnCancel);
    1.77 +    return S_OK;
    1.78 +  }
    1.79 +
    1.80 +  HRESULT hr;
    1.81 +  ComPtr<IStorageFile> file;
    1.82 +  hr = aFile->GetResults(file.GetAddressOf());
    1.83 +  // When the user cancels hr == S_OK and file is nullptr
    1.84 +  if (FAILED(hr) || !file) {
    1.85 +    if (mCallback)
    1.86 +      mCallback->Done(nsIFilePicker::returnCancel);
    1.87 +    return S_OK;
    1.88 +  }
    1.89 +  ComPtr<IStorageItem> storageItem;
    1.90 +  hr = file.As(&storageItem);
    1.91 +  if (FAILED(hr)) {
    1.92 +    if (mCallback)
    1.93 +      mCallback->Done(nsIFilePicker::returnCancel);
    1.94 +    return S_OK;
    1.95 +  }
    1.96 +  
    1.97 +  HSTRING path;
    1.98 +  if (FAILED(storageItem->get_Path(&path))) {
    1.99 +    if (mCallback)
   1.100 +      mCallback->Done(nsIFilePicker::returnCancel);
   1.101 +    return S_OK;
   1.102 +  }
   1.103 +  WindowsDuplicateString(path, mFilePath.GetAddressOf());
   1.104 +  WindowsDeleteString(path);
   1.105 +
   1.106 +  if (mCallback) {
   1.107 +    mCallback->Done(nsIFilePicker::returnOK);
   1.108 +  }
   1.109 +  return S_OK;
   1.110 +}
   1.111 +
   1.112 +HRESULT nsMetroFilePicker::OnPickMultipleFiles(IAsyncOperation<IVectorView<StorageFile*>*>* aFileList,
   1.113 +                                               AsyncStatus aStatus)
   1.114 +{
   1.115 +  if (aStatus != ABI::Windows::Foundation::AsyncStatus::Completed) {
   1.116 +    if (mCallback)
   1.117 +      mCallback->Done(nsIFilePicker::returnCancel);
   1.118 +    return S_OK;
   1.119 +  }
   1.120 +
   1.121 +  HRESULT hr;
   1.122 +  ComPtr<IVectorView<StorageFile*>> view;
   1.123 +  hr = aFileList->GetResults(view.GetAddressOf());
   1.124 +  if (FAILED(hr)) {
   1.125 +    if (mCallback)
   1.126 +      mCallback->Done(nsIFilePicker::returnCancel);
   1.127 +    return S_OK;
   1.128 +  }
   1.129 +
   1.130 +  unsigned int length;
   1.131 +  view->get_Size(&length);
   1.132 +  for (unsigned int idx = 0; idx < length; idx++) {
   1.133 +    ComPtr<IStorageFile> file;
   1.134 +    hr = view->GetAt(idx, file.GetAddressOf());
   1.135 +    if (FAILED(hr)) {
   1.136 +      continue;
   1.137 +    }
   1.138 +
   1.139 +    ComPtr<IStorageItem> storageItem;
   1.140 +    hr = file.As(&storageItem);
   1.141 +    if (FAILED(hr)) {
   1.142 +      continue;
   1.143 +    }
   1.144 +
   1.145 +    HSTRING path;
   1.146 +    if (SUCCEEDED(storageItem->get_Path(&path))) {
   1.147 +      nsCOMPtr<nsILocalFile> file = 
   1.148 +        do_CreateInstance("@mozilla.org/file/local;1");
   1.149 +      unsigned int tmp;
   1.150 +      if (NS_SUCCEEDED(file->InitWithPath(
   1.151 +          nsAutoString(WindowsGetStringRawBuffer(path, &tmp))))) {
   1.152 +        mFiles.AppendObject(file);
   1.153 +      }
   1.154 +    }
   1.155 +    WindowsDeleteString(path);
   1.156 +  }
   1.157 +
   1.158 +  if (mCallback) {
   1.159 +    mCallback->Done(nsIFilePicker::returnOK);
   1.160 +  }
   1.161 +  return S_OK;
   1.162 +}
   1.163 +
   1.164 +NS_IMETHODIMP
   1.165 +nsMetroFilePicker::Open(nsIFilePickerShownCallback *aCallback)
   1.166 +{
   1.167 +  HRESULT hr;
   1.168 +  // Capture a reference to the callback which we'll also pass into the
   1.169 +  // closure to ensure it's not freed.
   1.170 +  mCallback = aCallback;
   1.171 +
   1.172 +  // The filepicker cannot open when in snapped view, try to unsnapp
   1.173 +  // before showing the filepicker.
   1.174 +  ApplicationViewState viewState;
   1.175 +  MetroUtils::GetViewState(viewState);
   1.176 +  if (viewState == ApplicationViewState::ApplicationViewState_Snapped) {
   1.177 +    bool unsnapped = SUCCEEDED(MetroUtils::TryUnsnap());
   1.178 +    NS_ENSURE_TRUE(unsnapped, NS_ERROR_FAILURE);
   1.179 +  }
   1.180 +
   1.181 +  switch(mMode) {
   1.182 +  case nsIFilePicker::modeOpen: {
   1.183 +    NS_ENSURE_ARG_POINTER(mFileOpenPicker);
   1.184 +
   1.185 +    // Initiate the file picker operation
   1.186 +    ComPtr<IAsyncOperation<StorageFile*>> asyncOperation;
   1.187 +    hr = mFileOpenPicker->PickSingleFileAsync(asyncOperation.GetAddressOf());
   1.188 +    AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   1.189 +
   1.190 +    // Subscribe to the completed event
   1.191 +    ComPtr<IAsyncOperationCompletedHandler<StorageFile*>>
   1.192 +      completedHandler(Callback<IAsyncOperationCompletedHandler<StorageFile*>>(
   1.193 +        this, &nsMetroFilePicker::OnPickSingleFile));
   1.194 +    hr = asyncOperation->put_Completed(completedHandler.Get());
   1.195 +    AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
   1.196 +    break;
   1.197 +  }
   1.198 +
   1.199 +  case nsIFilePicker::modeOpenMultiple: {
   1.200 +    NS_ENSURE_ARG_POINTER(mFileOpenPicker);
   1.201 +
   1.202 +    typedef IVectorView<StorageFile*> StorageTemplate;
   1.203 +    typedef IAsyncOperation<StorageTemplate*> AsyncCallbackTemplate;
   1.204 +    typedef IAsyncOperationCompletedHandler<StorageTemplate*> HandlerTemplate;
   1.205 +
   1.206 +    // Initiate the file picker operation
   1.207 +    ComPtr<AsyncCallbackTemplate> asyncOperation;
   1.208 +    hr = mFileOpenPicker->PickMultipleFilesAsync(asyncOperation.GetAddressOf());
   1.209 +    AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   1.210 +
   1.211 +    // Subscribe to the completed event
   1.212 +    ComPtr<HandlerTemplate> completedHandler(Callback<HandlerTemplate>(
   1.213 +      this, &nsMetroFilePicker::OnPickMultipleFiles));
   1.214 +    hr = asyncOperation->put_Completed(completedHandler.Get());
   1.215 +    AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
   1.216 +    break;
   1.217 +  }
   1.218 +
   1.219 +  case nsIFilePicker::modeSave: {
   1.220 +    NS_ENSURE_ARG_POINTER(mFileSavePicker);
   1.221 +
   1.222 +    // Set the default file name
   1.223 +    mFileSavePicker->put_SuggestedFileName(HStringReference(mDefaultFilename.BeginReading()).Get());
   1.224 +
   1.225 +    // Set the default file extension
   1.226 +    if (mDefaultExtension.Length() > 0) {
   1.227 +      nsAutoString defaultFileExtension(mDefaultExtension);
   1.228 +
   1.229 +      // Touch up the extansion format platform hands us.
   1.230 +      if (defaultFileExtension[0] == L'*') {
   1.231 +        defaultFileExtension.Cut(0, 1);
   1.232 +      } else if (defaultFileExtension[0] != L'.') {
   1.233 +        defaultFileExtension.Insert(L".", 0);
   1.234 +      }
   1.235 +
   1.236 +      // Sometimes the default file extension is not passed in correctly,
   1.237 +      // so we purposfully ignore failures here.
   1.238 +      HString ext;
   1.239 +      ext.Set(defaultFileExtension.BeginReading());
   1.240 +      hr = mFileSavePicker->put_DefaultFileExtension(ext.Get());
   1.241 +      NS_ASSERTION(SUCCEEDED(hr), "put_DefaultFileExtension failed, bad format for extension?");
   1.242 +
   1.243 +      // Due to a bug in the WinRT file picker, the first file extension in the
   1.244 +      // list is always used when saving a file. So we explicitly make sure the
   1.245 +      // default extension is the first one in the list here.
   1.246 +      if (mFirstTitle.Get()) {
   1.247 +        ComPtr<IMap<HSTRING, IVector<HSTRING>*>> map;
   1.248 +        mFileSavePicker->get_FileTypeChoices(map.GetAddressOf());
   1.249 +        if (map) {
   1.250 +          boolean found = false;
   1.251 +          unsigned int index;
   1.252 +          map->HasKey(mFirstTitle.Get(), &found);
   1.253 +          if (found) {
   1.254 +            ComPtr<IVector<HSTRING>> list;
   1.255 +            if (SUCCEEDED(map->Lookup(mFirstTitle.Get(), list.GetAddressOf()))) {
   1.256 +              HString ext;
   1.257 +              ext.Set(defaultFileExtension.get());
   1.258 +              found = false;
   1.259 +              list->IndexOf(HStringReference(defaultFileExtension.get()).Get(), &index, &found);
   1.260 +              if (found) {
   1.261 +                list->RemoveAt(index);
   1.262 +                list->InsertAt(0, HStringReference(defaultFileExtension.get()).Get());
   1.263 +              }
   1.264 +            }
   1.265 +          }
   1.266 +        }
   1.267 +      }
   1.268 +    }
   1.269 +
   1.270 +    // Dispatch the async show operation
   1.271 +    ComPtr<IAsyncOperation<StorageFile*>> asyncOperation;
   1.272 +    hr = mFileSavePicker->PickSaveFileAsync(asyncOperation.GetAddressOf());
   1.273 +    AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   1.274 +
   1.275 +    // Subscribe to the completed event
   1.276 +    ComPtr<IAsyncOperationCompletedHandler<StorageFile*>>
   1.277 +      completedHandler(Callback<IAsyncOperationCompletedHandler<StorageFile*>>(
   1.278 +        this, &nsMetroFilePicker::OnPickSingleFile));
   1.279 +    hr = asyncOperation->put_Completed(completedHandler.Get());
   1.280 +    AssertRetHRESULT(hr, NS_ERROR_UNEXPECTED);
   1.281 +    break;
   1.282 +  }
   1.283 +
   1.284 +  case modeGetFolder:
   1.285 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.286 +
   1.287 +  default:
   1.288 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.289 +  }
   1.290 +  return NS_OK;
   1.291 +}
   1.292 +
   1.293 +NS_IMETHODIMP
   1.294 +nsMetroFilePicker::GetFile(nsIFile **aFile)
   1.295 +{
   1.296 +  NS_ENSURE_ARG_POINTER(aFile);
   1.297 +  *aFile = nullptr;
   1.298 +
   1.299 +  if (WindowsIsStringEmpty(mFilePath.Get()))
   1.300 +    return NS_OK;
   1.301 +
   1.302 +  nsCOMPtr<nsILocalFile> file(do_CreateInstance("@mozilla.org/file/local;1"));
   1.303 +  NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
   1.304 +  unsigned int length;
   1.305 +  file->InitWithPath(nsAutoString(mFilePath.GetRawBuffer(&length)));
   1.306 +  NS_ADDREF(*aFile = file);
   1.307 +  return NS_OK;
   1.308 +}
   1.309 +
   1.310 +NS_IMETHODIMP
   1.311 +nsMetroFilePicker::GetFileURL(nsIURI **aFileURL)
   1.312 +{
   1.313 +  *aFileURL = nullptr;
   1.314 +  nsCOMPtr<nsIFile> file;
   1.315 +  nsresult rv = GetFile(getter_AddRefs(file));
   1.316 +  if (!file)
   1.317 +    return rv;
   1.318 +
   1.319 +  return NS_NewFileURI(aFileURL, file);
   1.320 +}
   1.321 +
   1.322 +NS_IMETHODIMP
   1.323 +nsMetroFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
   1.324 +{
   1.325 +  NS_ENSURE_ARG_POINTER(aFiles);
   1.326 +  return NS_NewArrayEnumerator(aFiles, mFiles);
   1.327 +}
   1.328 +
   1.329 +// Set the filter index
   1.330 +NS_IMETHODIMP
   1.331 +nsMetroFilePicker::GetFilterIndex(int32_t *aFilterIndex)
   1.332 +{
   1.333 +  // No associated concept with a Metro file picker
   1.334 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.335 +}
   1.336 +
   1.337 +NS_IMETHODIMP
   1.338 +nsMetroFilePicker::SetFilterIndex(int32_t aFilterIndex)
   1.339 +{
   1.340 +  // No associated concept with a Metro file picker
   1.341 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.342 +}
   1.343 +
   1.344 +// AFACT, it's up to use to supply the implementation of a vector list.
   1.345 +class MozHStringVector : public RuntimeClass<IVector<HSTRING>> {
   1.346 +  InspectableClass(L"MozHStringVector", TrustLevel::BaseTrust)
   1.347 +  ~MozHStringVector() {
   1.348 +    Clear();
   1.349 +  }
   1.350 +
   1.351 +  // See IVector_impl in windows.foundation.collections.h
   1.352 +public:
   1.353 +  STDMETHOD(GetAt)(unsigned aIndex, HSTRING* aString) {
   1.354 +    if (aIndex >= mList.Length()) {
   1.355 +      return E_INVALIDARG;
   1.356 +    }
   1.357 +    return WindowsDuplicateString(mList[aIndex], aString);
   1.358 +  }
   1.359 +
   1.360 +  STDMETHOD(get_Size)(unsigned int* aLength) {
   1.361 +    if (!aLength) {
   1.362 +      return E_INVALIDARG;
   1.363 +    }
   1.364 +    *aLength = mList.Length();
   1.365 +    return S_OK;
   1.366 +  }
   1.367 +
   1.368 +  STDMETHOD(Append)(HSTRING aString) {
   1.369 +    HSTRING str;
   1.370 +    if (FAILED(WindowsDuplicateString(aString, &str))) {
   1.371 +      return E_INVALIDARG;
   1.372 +    }
   1.373 +    mList.AppendElement(str);
   1.374 +    return S_OK;
   1.375 +  }
   1.376 +
   1.377 +  STDMETHOD(Clear)() {
   1.378 +    int length = mList.Length();
   1.379 +    for (int idx = 0; idx < length; idx++)
   1.380 +      WindowsDeleteString(mList[idx]);
   1.381 +    mList.Clear();
   1.382 +    return S_OK;
   1.383 +  }
   1.384 +
   1.385 +  // interfaces picker code doesn't seem to need
   1.386 +  STDMETHOD(GetView)(IVectorView<HSTRING> **aView) { return E_NOTIMPL; }
   1.387 +  STDMETHOD(IndexOf)(HSTRING aValue, unsigned *aIndex, boolean *found) { return E_NOTIMPL; }
   1.388 +  STDMETHOD(SetAt)(unsigned aIndex, HSTRING aString) { return E_NOTIMPL; }
   1.389 +  STDMETHOD(InsertAt)(unsigned aIndex, HSTRING aString) { return E_NOTIMPL; }
   1.390 +  STDMETHOD(RemoveAt)(unsigned aIndex) { return E_NOTIMPL; }
   1.391 +  STDMETHOD(RemoveAtEnd)() { return E_NOTIMPL; }
   1.392 +
   1.393 +private:
   1.394 +  nsTArray<HSTRING> mList;
   1.395 +};
   1.396 +
   1.397 +nsresult
   1.398 +nsMetroFilePicker::ParseFiltersIntoVector(ComPtr<IVector<HSTRING>>& aVector,
   1.399 +                                          const nsAString& aFilter,
   1.400 +                                          bool aAllowAll)
   1.401 +{
   1.402 +  const char16_t *beg = aFilter.BeginReading();
   1.403 +  const char16_t *end = aFilter.EndReading();
   1.404 +  for (const char16_t *cur = beg, *fileTypeStart = beg; cur <= end; ++cur) {
   1.405 +    // Start of a a filetype, example: *.png
   1.406 +    if (cur == end || char16_t(' ') == *cur) {
   1.407 +      int32_t startPos = fileTypeStart - beg;
   1.408 +      int32_t endPos = cur - fileTypeStart - (cur == end ? 0 : 1);
   1.409 +      const nsAString& fileType = Substring(aFilter,
   1.410 +                                            startPos,
   1.411 +                                            endPos);
   1.412 +      // There is no way to say show all files in Metro save file picker, so
   1.413 +      // just use .data if * or *.* is specified.
   1.414 +      if (fileType.IsEmpty() ||
   1.415 +          fileType.Equals(L"*") ||
   1.416 +          fileType.Equals(L"*.*")) {
   1.417 +        HString str;
   1.418 +        if (aAllowAll) {
   1.419 +          str.Set(L"*");
   1.420 +          aVector->Append(str.Get());
   1.421 +        } else {
   1.422 +          str.Set(L".data");
   1.423 +          aVector->Append(str.Get());
   1.424 +        }
   1.425 +      } else {
   1.426 +        nsAutoString filter(fileType);
   1.427 +        if (filter[0] == L'*') {
   1.428 +          filter.Cut(0, 1);
   1.429 +        } else if (filter[0] != L'.') {
   1.430 +          filter.Insert(L".", 0);
   1.431 +        }
   1.432 +        HString str;
   1.433 +        str.Set(filter.BeginReading());
   1.434 +        aVector->Append(str.Get());
   1.435 +      }
   1.436 +
   1.437 +      fileTypeStart = cur + 1;
   1.438 +    }
   1.439 +  }
   1.440 +  return NS_OK;
   1.441 +}
   1.442 +
   1.443 +NS_IMETHODIMP
   1.444 +nsMetroFilePicker::AppendFilter(const nsAString& aTitle, 
   1.445 +                                const nsAString& aFilter)
   1.446 +{
   1.447 +  HRESULT hr;
   1.448 +  switch(mMode) {
   1.449 +  case nsIFilePicker::modeOpen:
   1.450 +  case nsIFilePicker::modeOpenMultiple: {
   1.451 +    NS_ENSURE_ARG_POINTER(mFileOpenPicker);
   1.452 +    ComPtr<IVector<HSTRING>> list;
   1.453 +    mFileOpenPicker->get_FileTypeFilter(list.GetAddressOf());
   1.454 +    nsresult rv = ParseFiltersIntoVector(list, aFilter, true);
   1.455 +    NS_ENSURE_SUCCESS(rv, rv);
   1.456 +  }
   1.457 +
   1.458 +  case nsIFilePicker::modeSave: {
   1.459 +    NS_ENSURE_ARG_POINTER(mFileSavePicker);
   1.460 +
   1.461 +    ComPtr<IMap<HSTRING,IVector<HSTRING>*>> map;
   1.462 +    hr = mFileSavePicker->get_FileTypeChoices(map.GetAddressOf());
   1.463 +    AssertRetHRESULT(hr, NS_ERROR_FAILURE);
   1.464 +
   1.465 +    HString key;
   1.466 +    key.Set(aTitle.BeginReading());
   1.467 +
   1.468 +    ComPtr<IVector<HSTRING>> saveTypes;
   1.469 +    saveTypes = Make<MozHStringVector>();
   1.470 +    nsresult rv = ParseFiltersIntoVector(saveTypes, aFilter, false);
   1.471 +    NS_ENSURE_SUCCESS(rv, rv);
   1.472 +
   1.473 +    if (WindowsIsStringEmpty(mFirstTitle.Get())) {
   1.474 +      mFirstTitle.Set(key.Get());
   1.475 +    }
   1.476 +
   1.477 +    boolean replaced;
   1.478 +    map->Insert(key.Get(), saveTypes.Get(), &replaced);
   1.479 +  }
   1.480 +  break;
   1.481 +
   1.482 +  default:
   1.483 +    return NS_ERROR_FAILURE;
   1.484 +  }
   1.485 +  return NS_OK;
   1.486 +}
   1.487 +

mercurial