Bug 1410214: Part 3 - Support packed WebExtension dictionaries. r=aswan,masayuki
authorKris Maglione <maglione.k@gmail.com>
Thu, 26 Apr 2018 13:26:05 -0700
changeset 472077 30448613bb72e41e386baf2e5e88cabd5ff720a8
parent 472076 3dd497b3e3f15b06e3e786d00addcd5ef85b97cb
child 472078 1ecf63f0cca6b2fa65edc893dd4ce51bbd979364
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan, masayuki
bugs1410214
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1410214: Part 3 - Support packed WebExtension dictionaries. r=aswan,masayuki MozReview-Commit-ID: 6nrw0IIe4UG
extensions/spellcheck/hunspell/glue/mozHunspell.cpp
extensions/spellcheck/hunspell/glue/mozHunspell.h
extensions/spellcheck/hunspell/glue/mozHunspellFileMgr.cpp
extensions/spellcheck/hunspell/glue/mozHunspellFileMgr.h
extensions/spellcheck/hunspell/tests/unit/test_hunspell_unicode_paths.js
extensions/spellcheck/hunspell/tests/unit/xpcshell.ini
extensions/spellcheck/idl/mozISpellCheckingEngine.idl
toolkit/components/extensions/Extension.jsm
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
@@ -69,16 +69,17 @@
 #include "mozISpellI18NManager.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozInlineSpellChecker.h"
 #include "mozilla/Services.h"
 #include <stdlib.h>
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
+#include "nsNetUtil.h"
 #include "mozilla/dom/ContentParent.h"
 
 using mozilla::dom::ContentParent;
 using namespace mozilla;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)
 
@@ -153,41 +154,24 @@ NS_IMETHODIMP mozHunspell::SetDictionary
     mAffixFileName.Truncate();
     mLanguage.Truncate();
     mDecoder = nullptr;
     mEncoder = nullptr;
 
     return NS_OK;
   }
 
-  nsIFile* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary));
+  nsIURI* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary));
   if (!affFile)
     return NS_ERROR_FILE_NOT_FOUND;
 
   nsAutoCString dictFileName, affFileName;
 
-#ifdef XP_WIN
-  nsAutoString affFileNameU;
-  nsresult rv = affFile->GetPath(affFileNameU);
+  nsresult rv = affFile->GetSpec(affFileName);
   NS_ENSURE_SUCCESS(rv, rv);
-  // Hunspell 1.3.3+ supports UTF-8 file paths on Windows
-  // by prefixing "\\\\?\\".
-  if (StringBeginsWith(affFileNameU, NS_LITERAL_STRING("\\\\"))) {
-    CopyUTF16toUTF8(affFileNameU, affFileName);
-    if (affFileNameU.CharAt(2) != u'?') {
-      affFileName.InsertLiteral("?\\UNC\\", 2);
-    }
-  } else {
-    affFileName.AssignLiteral("\\\\?\\");
-    AppendUTF16toUTF8(affFileNameU, affFileName);
-  }
-#else
-  nsresult rv = affFile->GetNativePath(affFileName);
-  NS_ENSURE_SUCCESS(rv, rv);
-#endif
 
   if (mAffixFileName.Equals(affFileName.get()))
     return NS_OK;
 
   dictFileName = affFileName;
   int32_t dotPos = dictFileName.RFindChar('.');
   if (dotPos == -1)
     return NS_ERROR_FAILURE;
@@ -198,17 +182,17 @@ NS_IMETHODIMP mozHunspell::SetDictionary
   // SetDictionary can be called multiple times, so we might have a
   // valid mHunspell instance which needs cleaned up.
   delete mHunspell;
 
   mDictionary = aDictionary;
   mAffixFileName = affFileName;
 
   mHunspell = new Hunspell(affFileName.get(),
-                         dictFileName.get());
+                           dictFileName.get());
   if (!mHunspell)
     return NS_ERROR_OUT_OF_MEMORY;
 
   auto encoding =
     Encoding::ForLabelNoReplacement(mHunspell->get_dict_encoding());
   if (!encoding) {
     return NS_ERROR_UCONV_NOCONV;
   }
@@ -483,17 +467,21 @@ mozHunspell::LoadDictionariesFromDir(nsI
 
 #ifdef DEBUG_bsmedberg
     printf("Adding dictionary: %s\n", NS_ConvertUTF16toUTF8(dict).get());
 #endif
 
     // Replace '_' separator with '-'
     dict.ReplaceChar("_", '-');
 
-    mDictionaries.Put(dict, file);
+    nsCOMPtr<nsIURI> uri;
+    rv = NS_NewFileURI(getter_AddRefs(uri), file);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mDictionaries.Put(dict, uri);
   }
 
   return NS_OK;
 }
 
 nsresult
 mozHunspell::ConvertCharset(const char16_t* aStr, std::string* aDst)
 {
@@ -662,30 +650,30 @@ NS_IMETHODIMP mozHunspell::RemoveDirecto
     obs->NotifyObservers(nullptr,
                          SPELLCHECK_DICTIONARY_REMOVE_NOTIFICATION,
                          nullptr);
   }
 #endif
   return NS_OK;
 }
 
-NS_IMETHODIMP mozHunspell::AddDictionary(const nsAString& aLang, nsIFile *aFile)
+NS_IMETHODIMP mozHunspell::AddDictionary(const nsAString& aLang, nsIURI *aFile)
 {
   NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG);
 
   mDynamicDictionaries.Put(aLang, aFile);
   mDictionaries.Put(aLang, aFile);
   DictionariesChanged(true);
   return NS_OK;
 }
 
-NS_IMETHODIMP mozHunspell::RemoveDictionary(const nsAString& aLang, nsIFile *aFile)
+NS_IMETHODIMP mozHunspell::RemoveDictionary(const nsAString& aLang, nsIURI *aFile)
 {
   NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG);
 
-  nsCOMPtr<nsIFile> file = mDynamicDictionaries.Get(aLang);
+  nsCOMPtr<nsIURI> file = mDynamicDictionaries.Get(aLang);
   bool equal;
   if (file && NS_SUCCEEDED(file->Equals(aFile, &equal)) && equal) {
     mDynamicDictionaries.Remove(aLang);
     LoadDictionaryList(true);
   }
   return NS_OK;
 }
--- a/extensions/spellcheck/hunspell/glue/mozHunspell.h
+++ b/extensions/spellcheck/hunspell/glue/mozHunspell.h
@@ -63,16 +63,17 @@
 #include <hunspell.hxx>
 #include "mozISpellCheckingEngine.h"
 #include "mozIPersonalDictionary.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObserver.h"
+#include "nsIURI.h"
 #include "mozilla/Encoding.h"
 #include "nsInterfaceHashtable.h"
 #include "nsWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozHunspellAllocator.h"
 
 #define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1"
 #define MOZ_HUNSPELL_CID         \
@@ -107,21 +108,21 @@ protected:
 
   void DictionariesChanged(bool aNotifyChildProcesses);
 
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
   mozilla::UniquePtr<mozilla::Encoder> mEncoder;
   mozilla::UniquePtr<mozilla::Decoder> mDecoder;
 
   // Hashtable matches dictionary name to .aff file
-  nsInterfaceHashtable<nsStringHashKey, nsIFile> mDictionaries;
+  nsInterfaceHashtable<nsStringHashKey, nsIURI> mDictionaries;
   nsString  mDictionary;
   nsString  mLanguage;
   nsCString mAffixFileName;
 
   // dynamic dirs used to search for dictionaries
   nsCOMArray<nsIFile> mDynamicDirectories;
-  nsInterfaceHashtable<nsStringHashKey, nsIFile> mDynamicDictionaries;
+  nsInterfaceHashtable<nsStringHashKey, nsIURI> mDynamicDictionaries;
 
   Hunspell  *mHunspell;
 };
 
 #endif
--- a/extensions/spellcheck/hunspell/glue/mozHunspellFileMgr.cpp
+++ b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgr.cpp
@@ -5,37 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozHunspellFileMgr.h"
 
 #include "mozilla/DebugOnly.h"
 #include "nsContentUtils.h"
 #include "nsILoadInfo.h"
 #include "nsNetUtil.h"
-#include "nsXPCOM.h"
 
 using namespace mozilla;
 
 FileMgr::FileMgr(const char* aFilename, const char* aKey)
 {
   DebugOnly<Result<Ok, nsresult>> result = Open(nsDependentCString(aFilename));
   NS_WARNING_ASSERTION(result.value.isOk(), "Failed to open Hunspell file");
 }
 
 Result<Ok, nsresult>
 FileMgr::Open(const nsACString& aPath)
 {
   nsCOMPtr<nsIURI> uri;
-
-  nsresult rv = NS_NewURI(getter_AddRefs(uri), aPath);
-  if (NS_FAILED(rv)) {
-    nsCOMPtr<nsIFile> file;
-    MOZ_TRY(NS_NewNativeLocalFile(aPath, false, getter_AddRefs(file)));
-    MOZ_TRY(NS_NewFileURI(getter_AddRefs(uri), file));
-  }
+  MOZ_TRY(NS_NewURI(getter_AddRefs(uri), aPath));
 
   nsCOMPtr<nsIChannel> channel;
   MOZ_TRY(NS_NewChannel(
     getter_AddRefs(channel),
     uri,
     nsContentUtils::GetSystemPrincipal(),
     nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
     nsIContentPolicy::TYPE_OTHER));
--- a/extensions/spellcheck/hunspell/glue/mozHunspellFileMgr.h
+++ b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgr.h
@@ -15,18 +15,17 @@
 #include "nsReadLine.h"
 
 // Note: This class name and lack of namespacing terrible, but are necessary
 // for Hunspell compatibility.
 class FileMgr final
 {
 public:
   /**
-   * aFilename may be the native filesystem path or local file/jar URI for the
-   * file to load.
+   * aFilename must be a local file/jar URI for the file to load.
    *
    * aKey is the decription key for encrypted Hunzip files, and is
    * unsupported. The argument is there solely for compatibility.
    */
   explicit FileMgr(const char* aFilename, const char* aKey = nullptr);
   ~FileMgr() = default;
 
   // Note: The nonstandard naming conventions of these methods are necessary for
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/tests/unit/test_hunspell_unicode_paths.js
@@ -0,0 +1,34 @@
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/osfile.jsm");
+
+Cu.importGlobalProperties(["TextEncoder"]);
+
+XPCOMUtils.defineLazyServiceGetter(this, "spellCheck",
+                                   "@mozilla.org/spellchecker/engine;1", "mozISpellCheckingEngine");
+
+const nsFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
+                                      "initWithPath");
+
+add_task(async function() {
+  let prof = do_get_profile();
+
+  let basePath = OS.Path.join(prof.path, "\u263a", "dictionaries");
+  let baseDir = nsFile(basePath);
+  await OS.File.makeDir(basePath, {from: prof.path});
+
+  let dicPath = OS.Path.join(basePath, "dict.dic");
+  let affPath = OS.Path.join(basePath, "dict.aff");
+
+  const WORD = "Flehgragh";
+
+  await OS.File.writeAtomic(dicPath, new TextEncoder().encode(`1\n${WORD}\n`));
+  await OS.File.writeAtomic(affPath, new TextEncoder().encode(""));
+
+  spellCheck.loadDictionariesFromDir(baseDir);
+  spellCheck.dictionary = "dict";
+
+  ok(spellCheck.check(WORD), "Dictionary should have been loaded from a unicode path");
+});
+
--- a/extensions/spellcheck/hunspell/tests/unit/xpcshell.ini
+++ b/extensions/spellcheck/hunspell/tests/unit/xpcshell.ini
@@ -1,6 +1,8 @@
 [DEFAULT]
 head =
 skip-if = toolkit == 'android'
 support-files = data/**
+firefox-appdir = browser
 
 [test_hunspell.js]
+[test_hunspell_unicode_paths.js]
--- a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl
+++ b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIFile;
+interface nsIURI;
 interface mozIPersonalDictionary;
 
 [scriptable, uuid(8ba643a4-7ddc-4662-b976-7ec123843f10)]
 
 /**
  * This interface represents a SpellChecker.
  */
 
@@ -82,26 +83,26 @@ interface mozISpellCheckingEngine : nsIS
   void addDirectory(in nsIFile dir);
 
   /**
    * Remove dictionaries from a directory from the spell checker
    */
   void removeDirectory(in nsIFile dir);
 
   /**
-   * Add a dictionary with the given language code and file path.
+   * Add a dictionary with the given language code and file URI.
    */
-  void addDictionary(in AString lang, in nsIFile dir);
+  void addDictionary(in AString lang, in nsIURI file);
 
   /**
    * Remove a dictionary with the given language code and path. If the path does
    * not match that of the current entry with the given language code, it is not
    * removed.
    */
-  void removeDictionary(in AString lang, in nsIFile dir);
+  void removeDictionary(in AString lang, in nsIURI file);
 };
 
 %{C++
 #define DICTIONARY_SEARCH_DIRECTORY "DictD"
 #define DICTIONARY_SEARCH_DIRECTORY_LIST "DictDL"
 
 #define SPELLCHECK_DICTIONARY_REMOVE_NOTIFICATION \
   "spellcheck-dictionary-remove"
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1869,20 +1869,20 @@ class Dictionary extends ExtensionData {
 
   static getBootstrapScope(id, file) {
     return new DictionaryBootstrapScope();
   }
 
   async startup(reason) {
     this.dictionaries = {};
     for (let [lang, path] of Object.entries(this.startupData.dictionaries)) {
-      let {file} = Services.io.newURI(path, null, this.rootURI).QueryInterface(Ci.nsIFileURL);
-      this.dictionaries[lang] = file;
+      let uri = Services.io.newURI(path, null, this.rootURI);
+      this.dictionaries[lang] = uri;
 
-      spellCheck.addDictionary(lang, file);
+      spellCheck.addDictionary(lang, uri);
     }
 
     Management.emit("ready", this);
   }
 
   async shutdown(reason) {
     if (reason !== "APP_SHUTDOWN") {
       for (let [lang, file] of Object.entries(this.dictionaries)) {
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -320,17 +320,17 @@ class AddonInternal {
     }
 
     if (this.signedState === AddonManager.SIGNEDSTATE_NOT_REQUIRED)
       return true;
     return this.signedState > AddonManager.SIGNEDSTATE_MISSING;
   }
 
   get unpack() {
-    return this.type === "dictionary" || this.type === "webextension-dictionary";
+    return this.type === "dictionary";
   }
 
   get isCompatible() {
     return this.isCompatibleWith();
   }
 
   get disabled() {
     return (this.userDisabled || this.appDisabled || this.softDisabled);
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-unpack.ini
@@ -7,10 +7,9 @@ dupe-manifest =
 tags = addons
 
 [test_webextension_paths.js]
 tags = webextensions
 [test_webextension_theme.js]
 tags = webextensions
 
 [test_dictionary.js]
-[test_dictionary_webextension.js]
 [test_filepointer.js]
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -86,16 +86,17 @@ run-if = addon_signing
 [test_db_path.js]
 head =
 [test_default_providers_pref.js]
 [test_delay_update.js]
 [test_delay_update_webextension.js]
 skip-if = appname == "thunderbird"
 tags = webextensions
 [test_dependencies.js]
+[test_dictionary_webextension.js]
 [test_distribution.js]
 [test_duplicateplugins.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_error.js]
 [test_ext_management.js]
 skip-if = appname == "thunderbird"
 tags = webextensions