hechengjin 发表于 2016-3-27 23:38:12

通讯录导入功能流程跟踪


/////js调用导入csv格式导入通讯录////////
var importService = 0;
      importService = Components.classes["@mozilla.org/import/import-service;1"]
                                    .getService(Components.interfaces.nsIImportService);
    var module = 0;
   if (exprotType == 'vcard')
          {
            
             module = importService.GetModule("addressbook", 0);
          }
          else//csv
          {
            module = importService.GetModule("addressbook", 2);
          }

    addInterface = module.GetImportInterface( "addressbook");
          if (addInterface != null)
            addInterface = addInterface.QueryInterface( Components.interfaces.nsIImportGeneric);
          if (addInterface == null) {
            var errorText = bundle_import.GetStringFromName('ImportAddressBadModule');
            var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);

            promptService.alert(window, document.title, errorText);
            return( false);
          }

    addInterface.SetData("addressLocation", file);

var map = addInterface.GetData( "fieldMap");
          if (map != null) {
            map = map.QueryInterface( Components.interfaces.nsIImportFieldMap);
            if (map != null) {
            var result = new Object();
            result.ok = false;
            top.window.openDialog(
                "chrome://messenger/content/fieldMapImport.xul",
                "",
                "chrome,modal,titlebar",
                {fieldMap: map,
               addInterface: addInterface,
               result: result});
            }
            if (result.ok == false)
            return( false);
          }


//fieldMapImport.xul确定按钮影响
function FieldImportOKButton()
{
var max = gListbox.getRowCount();
var fIndex;
var on;
// Ensure field map is the right size
top.fieldMap.SetFieldMapSize(max);

for (var i = 0; i < max; i++) {
    fIndex = gListbox.getItemAtIndex(i).getAttribute( 'field-index');
    on = gListbox.getItemAtIndex(i).firstChild.getAttribute('checked');
    top.fieldMap.SetFieldMap( i, fIndex);
    top.fieldMap.SetFieldActive( i, (on == "true"));
}

top.fieldMap.skipFirstRecord = gSkipFirstRecordButton.checked;

top.dialogResult.ok = true;

return true;
}


hechengjin 发表于 2016-3-27 23:38:41


nsIImportService.idl

#include "nsISupports.idl"
#include "nsIMsgSend.idl"

interface nsIImportModule;
interface nsIImportMailboxDescriptor;
interface nsIImportABDescriptor;
interface nsIImportGeneric;
interface nsIImportFieldMap;
interface nsIMsgSendListener;
interface nsIMsgCompFields;
interface nsIMsgSendListener;
interface nsIArray;
/*
*scriptable:声明该接口可以被javascript使用,且必须从一个scriptable接口集成而来,接口使用的变量类型必须是可以javascript化的。
*idl接口可使用的类型:XPIDL types
*///

interface nsIImportService : nsISupports
{
    void DiscoverModules();

long GetModuleCount( in string filter);
void GetModuleInfo( in string filter, in long index, out wstring name, out wstring description);
wstring GetModuleName( in string filter, in long index);
wstring GetModuleDescription( in string filter, in long index);
nsIImportModuleGetModule( in string filter, in long index);
nsIImportModule GetModuleWithCID( in nsCIDRef cid);

nsIImportFieldMap      CreateNewFieldMap();
nsIImportMailboxDescriptorCreateNewMailboxDescriptor();
nsIImportABDescriptor    CreateNewABDescriptor();
nsIImportGeneric      CreateNewGenericMail();
nsIImportGeneric      CreateNewGenericAddressBooks();
void CreateRFC822Message(in nsIMsgIdentity aIdentity,
                           in nsIMsgCompFields aMsgFields,
                           in string aBodytype,
                           in string aBody,
                           in unsigned long aBodyLength,
                           in boolean aCreateAsDraft,
                           in nsIArray aLoadedAttachments,
                           in nsISupportsArray aEmbeddedObjects,
                           in nsIMsgSendListener aListener);

};

%{ C++
#define NS_IMPORTSERVICE_CID            \
{ /* 5df96d60-1726-11d3-a206-00a0cc26da63 */      \
   0x5df96d60, 0x1726, 0x11d3,                   \
   {0xa2, 0x06, 0x0, 0xa0, 0xcc, 0x26, 0xda, 0x63}}

#define NS_IMPORTSERVICE_CONTRACTID "@mozilla.org/import/import-service;1"
%}

///////////////实现////////
nsImportService.h

class nsImportService : public nsIImportService
{
public:

nsImportService();
virtual ~nsImportService();

NS_DECL_ISUPPORTS

    NS_DECL_NSIIMPORTSERVICE

private:
    nsresult LoadModuleInfo(const char*pClsId, const char *pSupports);
nsresult DoDiscover(void);

private:
    nsImportModuleList * m_pModules;
bool m_didDiscovery;
nsCString m_sysCharset;
nsIUnicodeDecoder * m_pDecoder;
nsIUnicodeEncoder * m_pEncoder;
nsCOMPtr<nsIStringBundle> m_stringBundle;
};

nsImportService.cpp

PRLogModuleInfo *IMPORTLOGMODULE = nullptr;//日志模块

static nsIImportService *gImportService = nullptr; //单例 什么时候创建?

nsImportService::nsImportService() : m_pModules(nullptr)
{
// Init logging module.
if (!IMPORTLOGMODULE)
    IMPORTLOGMODULE = PR_NewLogModule("IMPORT");//日志模块配置
IMPORT_LOG0("* nsImport Service Created\n");

m_didDiscovery = false;
m_pDecoder = nullptr;
m_pEncoder = nullptr;
///#define IMPORT_MSGS_URL       "chrome://messenger/locale/importMsgs.properties"
nsresult rv = nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle));
if (NS_FAILED(rv))
    IMPORT_LOG0("Failed to get string bundle for Importing Mail");
}


nsImportService::~nsImportService()
{
NS_IF_RELEASE(m_pDecoder);
NS_IF_RELEASE(m_pEncoder);

gImportService = nullptr;

    if (m_pModules != nullptr)
      delete m_pModules;

IMPORT_LOG0("* nsImport Service Deleted\n");
}


NS_IMETHODIMP nsImportService::GetModule(const char *filter, int32_t index, nsIImportModule **_retval)
{
    NS_PRECONDITION(_retval != nullptr, "null ptr");
    if (!_retval)
      return NS_ERROR_NULL_POINTER;
*_retval = nullptr;

    DoDiscover();
    if (!m_pModules)
    return NS_ERROR_FAILURE;

if ((index < 0) || (index >= m_pModules->GetCount()))
    return NS_ERROR_FAILURE;

ImportModuleDesc *pDesc;
int32_tcount = 0;
for (int32_t i = 0; i < m_pModules->GetCount(); i++) {
    pDesc = m_pModules->GetModuleDesc(i);
    if (pDesc->SupportsThings(filter)) {
      if (count == index) {
      *_retval = pDesc->GetModule();
      break;
      }
      else
      count++;
    }
}
if (! (*_retval))
    return NS_ERROR_FAILURE;

return NS_OK;
}

nsresult nsImportService:: LoadModuleInfo(const char *pClsId, const char *pSupports)
{
if (!pClsId || !pSupports)
    return NS_OK;

if (m_pModules == nullptr)
    m_pModules = new nsImportModuleList();

// load the component and get all of the info we need from it....
// then call AddModule
nsresultrv;

nsCID      clsId;
clsId.Parse(pClsId);
nsIImportModule *module;
rv = CallCreateInstance(clsId, &module);
if (NS_FAILED(rv)) return rv;

nsStringtheTitle;
nsStringtheDescription;
rv = module->GetName(getter_Copies(theTitle));
if (NS_FAILED(rv))
    theTitle.AssignLiteral("Unknown");

rv = module->GetDescription(getter_Copies(theDescription));
if (NS_FAILED(rv))
    theDescription.AssignLiteral("Unknown description");

// call the module to get the info we need
m_pModules->AddModule(clsId, pSupports, theTitle.get(), theDescription.get());

module->Release();

return NS_OK;
}

nsresult nsImportService:oDiscover(void)
{
if (m_didDiscovery)
    return NS_OK;

if (m_pModules != nullptr)
    m_pModules->ClearList();

nsresult rv;

nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);

nsCOMPtr<nsISimpleEnumerator> e;
rv = catMan->EnumerateCategory("mailnewsimport", getter_AddRefs(e));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupportsCString> contractid;
rv = e->GetNext(getter_AddRefs(contractid));
while (NS_SUCCEEDED(rv) && contractid)
{
    nsCString contractIdStr;
    contractid->ToString(getter_Copies(contractIdStr));
    nsCString supportsStr;
    rv = catMan->GetCategoryEntry("mailnewsimport", contractIdStr.get(), getter_Copies(supportsStr));
    if (NS_SUCCEEDED(rv))
      LoadModuleInfo(contractIdStr.get(), supportsStr.get());
    rv = e->GetNext(getter_AddRefs(contractid));
}

m_didDiscovery = true;

return NS_OK;
}

hechengjin 发表于 2016-3-27 23:39:00

导入服务类支持不同模块的导入:
如:
module = importService.GetModule("addressbook", 0);//vcard
module = importService.GetModule("addressbook", 2);//csv
模块列表为 m_pModules = new nsImportModuleList();中
其内部真正实现导入功能的是nsIImportModule *m_pModule;

各模块的初始加载通过 nsresult nsImportService:: DoDiscover(void) -->LoadModuleInfo(contractIdStr.get(), supportsStr.get()); -->m_pModules->AddModule(clsId, pSupports, theTitle.get(), theDescription.get());


importService.GetModule("addressbook", 2);//csv 时触发 DoDiscover    nsCOMPtr<nsICategoryManager> catMan

//m_pModules列表内容
pSupports | theTitle | theDescription
[ "addressbook" | "vCard 文件 (.vcf)" | "从 vCard 格式导入一个地址簿" ]
[ "mail,addressbook,settings" | "Outlook" | "Outlook 邮件,通讯录和设置" ]
[ "settings" | "Windows Live Mail" | "Windows Live Mail 设置" ]
[ "addressbook" | "文本" | "从文本文件导入通讯录。包括:LDIF (.ldif, .ldi),tab 定界的格式或以逗号分隔的 (.csv) 格式。" ]
[ "mail,addressbook,settings" | "Outlook Express" | "Outlook Express 邮件,通讯录和设置" ]
[ "mail,addressbook,settings,filters" | "Eudora" | "Eudora 邮件,通讯录和设置" ]
catMan内容的初始化来自于组件注册,如下调用堆栈
         xul.dll!nsCategoryManager::AddCategoryEntry(const char * aCategoryName=0x01d17c8c, const char * aEntryName=0x01cfba18, const char * aValue=0x01f6995c, bool aReplace=true, char * * aOldValue=0x00000000)
         xul.dll!nsComponentManagerImpl::RegisterModule(const mozilla::Module * aModule=0x01d18504, mozilla::FileLocation * aFile=0x021d2078)行 413 + 0x16 字节      C++
      xul.dll!nsComponentManagerImpl::Init()行 350 + 0xe 字节      C++
         xul.dll!NS_InitXPCOM2_P(nsIServiceManager * * result=0x021a6fc0, nsIFile * binDirectory=0x003d6be0, nsIDirectoryServiceProvider * appFileLocationProvider=0x0012fca8)行 466 + 0xb 字节      C++
         xul.dll!ScopedXPCOMStartup::Initialize()行 1174 + 0x1b 字节      C++
         xul.dll!XREMain::XRE_main(int argc=1, char * * argv=0x003d4570, const nsXREAppData * aAppData=0x003d6870)行 3894      C++





所有这些与导入相关的模块都可在 nsImportModule.cpp中找到-----导入相关的服务模块 thinkmail\mailnews\import
static const mozilla::Module kMailNewsImportModule = {
mozilla::Module::kVersion,
kMailNewsImportCIDs,
kMailNewsImportContracts,
kMailNewsImportCategories,
NULL,
NULL,
importModuleDtor
};

importService.GetModule("addressbook", 2);   // rv = CallCreateInstance(m_cid, &m_pModule);//{A5991D01-ADA7-11d3-A9C2-00A0CC26DA63}这里主要用到的是导入模块列表m_pModules中的cid为{A5991D01-ADA7-11d3-A9C2-00A0CC26DA63}的,即:2 [ "addressbook" | "文本" | "从文本文件导入通讯录。包括:LDIF (.ldif, .ldi),tab 定界的格式或以逗号分隔的 (.csv) 格式。" ]
////////////////////////////////////////////////////////////////////////////////
// text import factories
////////////////////////////////////////////////////////////////////////////////
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextImport)

即可找到其对应实现
nsTextImport.h   nsTextImport.cpp
class nsTextImport : public nsIImportModule
{
public:
nsTextImport();
virtual ~nsTextImport();

NS_DECL_ISUPPORTS

////////////////////////////////////////////////////////////////////////////////////////
// we suppport the nsIImportModule interface
////////////////////////////////////////////////////////////////////////////////////////

NS_DECL_NSIIMPORTMODULE

protected:
nsCOMPtr<nsIStringBundle>   m_stringBundle;
};
addInterface = module.GetImportInterface( "addressbook");//返回指向nsIImportGeneric 实现的kISupportsIID接口

NS_IMETHODIMP nsTextImport::GetImportInterface(const char *pImportType, nsISupports **ppInterface)
{
if (!strcmp(pImportType, "addressbook")) {
    // create the nsIImportMail interface and return it!
    nsIImportAddressBooks * pAddress = nullptr;
    nsIImportGeneric * pGeneric = nullptr;
    rv = ImportAddressImpl::Create(&pAddress, m_stringBundle);   //pAddress = new ImportAddressImpl(aStringBundle);
    if (NS_SUCCEEDED(rv)) {
      nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
      if (NS_SUCCEEDED(rv)) {
      rv = impSvc->CreateNewGenericAddressBooks(&pGeneric);// new nsImportGenericAddressBooks();   pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void **)aImportGeneric);
      if (NS_SUCCEEDED(rv)) {
          pGeneric->SetData("addressInterface", pAddress);// item->QueryInterface(NS_GET_IID(nsIImportAddressBooks), (void **) &m_pInterface);
          rv = pGeneric->QueryInterface(kISupportsIID, (void **)ppInterface);
      }
      }
    }
}

this.addInterface = module.GetImportInterface( "addressbook"); //内部记录到通讯录字段//nsIImportAddressBooksm_pInterface nsImportGenericAddressBooks
this.addInterface.SetData("addressLocation", file);//内部通讯录字面和文件关联//m_pLocation    m_pInterface->SetSampleLocation(m_pLocation); ImportAddressImpl


NS_IMETHODIMP nsImportGenericAddressBooks::SetData(const char *dataId, nsISupports *item)
{
NS_PRECONDITION(dataId != nullptr, "null ptr");
if (!dataId)
    return NS_ERROR_NULL_POINTER;

if (!PL_strcasecmp(dataId, "addressInterface")) {
    NS_IF_RELEASE(m_pInterface);
    if (item)
      item->QueryInterface(NS_GET_IID(nsIImportAddressBooks), (void **) &m_pInterface);
}
if (!PL_strcasecmp(dataId, "addressBooks")) {
    NS_IF_RELEASE(m_pBooks);
    if (item)
      item->QueryInterface(NS_GET_IID(nsISupportsArray), (void **) &m_pBooks);
}

if (!PL_strcasecmp(dataId, "addressLocation")) {
    m_pLocation = nullptr;
    if (item) {
      nsresult rv;
      m_pLocation = do_QueryInterface(item, &rv);
      NS_ENSURE_SUCCESS(rv,rv);
    }
    if (m_pInterface)
      m_pInterface->SetSampleLocation(m_pLocation);
}

if (!PL_strcasecmp(dataId, "addressDestination")) {
    if (item) {
      nsCOMPtr<nsISupportsCString> abString;
      item->QueryInterface(NS_GET_IID(nsISupportsCString), getter_AddRefs(abString));
      if (abString) {
      if (m_pDestinationUri)
          NS_Free(m_pDestinationUri);
      m_pDestinationUri = nullptr;
                nsCAutoString tempUri;
                abString->GetData(tempUri);
                m_pDestinationUri = ToNewCString(tempUri);
      }
    }
}
if (!PL_strcasecmp(dataId, "fieldMap")) {
    NS_IF_RELEASE(m_pFieldMap);
    if (item)
      item->QueryInterface(NS_GET_IID(nsIImportFieldMap), (void **) &m_pFieldMap);
}
return NS_OK;
}

this.addInterface nsImportGenericAddressBooks
this.fieldMap = addInterface.GetData( "fieldMap");   //m_pFieldMap m_pInterface[通讯录接口]   m_pLocation[文件接口]


NS_IMETHODIMP nsImportGenericAddressBooks::GetData(const char *dataId, nsISupports **_retval)
{
NS_PRECONDITION(_retval != nullptr, "null ptr");
if (!_retval)
    return NS_ERROR_NULL_POINTER;

nsresult rv;
*_retval = nullptr;

if (!PL_strcasecmp(dataId, "fieldMap")) {
    if (m_pFieldMap) {
      *_retval = m_pFieldMap;
      m_pFieldMap->AddRef();
    }
    else {
      if (m_pInterface && m_pLocation) {
      bool needsIt = false;
      m_pInterface->GetNeedsFieldMap(m_pLocation, &needsIt); //这里判断是不是isLDIF文件,来确认要不要打开映射关系窗口
      if (needsIt) {
          GetDefaultFieldMap(); //这里通过代码中设置的要映射的字段赋初值 ,然后再根据用户以前使用过的先清空默认,再调整为mailnews.import.text.fieldmap中记录的
          if (m_pFieldMap) {
            *_retval = m_pFieldMap;
            m_pFieldMap->AddRef();
          }
      }
      }
    }
}
return NS_OK;
}

this.fieldMap.SetFieldMapSize(max);
this.fieldMap.SetFieldMap( this._listHandledInfoMap.abIndex, tindex); //先abindex 后txtindex?         m_pFields = fieldNum;index(ab)-->fieldNum(txt)
this.fieldMap.SetFieldActive( this._listHandledInfoMap.abIndex, this._listHandledInfoMap.matchOK);m_pActive = active;//如个ab字段是有效匹配的

this.addInterface.WantsProgress()
this.addInterface.BeginImport(success, error);

NS_IMETHODIMP nsImportGenericAddressBooks::WantsProgress(bool *_retval)
{
}
NS_IMETHODIMP nsImportGenericAddressBooks::BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval)
{
}

hechengjin 发表于 2016-3-27 23:39:20

模块列表定义

class nsImportModuleList {
public:
nsImportModuleList() { m_pList = nullptr; m_alloc = 0; m_count = 0;}
~nsImportModuleList() { ClearList(); }

voidAddModule(const nsCID& cid, const char *pSupports, const PRUnichar *pName, const PRUnichar *pDesc);

voidClearList(void);

int32_tGetCount(void) { return m_count;}

ImportModuleDesc *GetModuleDesc(int32_t idx)
    { if ((idx < 0) || (idx >= m_count)) return nullptr; else return m_pList;}

private:

private:
    ImportModuleDesc **m_pList;
int32_t      m_alloc;
int32_t      m_count;
};


class ImportModuleDesc {
public:
ImportModuleDesc() { m_pModule = nullptr;}
~ImportModuleDesc() { ReleaseModule();}

voidSetCID(const nsCID& cid) { m_cid = cid;}
voidSetName(const PRUnichar *pName) { m_name = pName;}
voidSetDescription(const PRUnichar *pDesc) { m_description = pDesc;}
voidSetSupports(const char *pSupports) { m_supports = pSupports;}

nsCID      GetCID(void) { return m_cid;}
const PRUnichar *GetName(void) { return m_name.get();}
const PRUnichar *GetDescription(void) { return m_description.get();}
const char *GetSupports(void) { return m_supports.get();}

nsIImportModule *GetModule(bool keepLoaded = false); // Adds ref
void      ReleaseModule(void);

bool      SupportsThings(const char *pThings);

private:
    nsCID m_cid;
nsString m_name;
nsString m_description;
nsCString m_supports;
nsIImportModule *m_pModule;
};

hechengjin 发表于 2016-3-27 23:39:41

module = importService.GetModule("addressbook", 0);//vcard
[ "addressbook" | "vCard 文件 (.vcf)" | "从 vCard 格式导入一个地址簿" ]

{0eb034a3-964a-4e2f-92eb-cc55d9ae9dd2}

firemail\mailnews\import\vcard\src\nsVCardImport.cpp
NS_IMETHODIMP nsVCardImport::GetImportInterface(
页: [1]
查看完整版本: 通讯录导入功能流程跟踪