<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><lock aspectratio="t" v:ext="edit"></lock></shapetype> | Mozilla扩展系列链接: 1,浅谈基于Mozilla Thunderbird的扩展开发
2,基于Mozilla平台的扩展开发(续)----XPCOM组件篇 3,基于Mozilla Thunderbird的扩展开发(三)---如何获取邮件的完整信息 4,基于Mozilla Thunderbird的扩展开发(四)---修改源代码实现自动保存附件 5,基于Mozilla Thunderbird的扩展开发(五)---进程间通信之Socket篇(上) |
Mozilla最为人诟病的地方就是没有称手的开发工具,这对于我们这些被微软惯坏的开发人员来说,如果没有Visual Studio这样舒服的工具的话,谁会投入你的怀抱呢?本文就希望从三个方面介绍下我所了解到的Mozilla平台下的开发工具及一些小技巧。
最原始的方法当然是全部手工打造,从插件的编写,到打包,这样做的好处是对Mozilla平台下插件的开发有比较深入的了解,但缺点就是每次做一些小的修改后都需要重复进行压缩,打包等一系列繁杂的工序,如果你喜欢这种方法,可以参考我这篇文章:《浅谈基于Mozilla ThunderBird的扩展开发》,也可以参考Mozilla开发中心这篇文章《Building an Extension》。
每次修改代码后就得自己手动重新打包,发布,安装,你对这些已经感到厌烦了吗?好的,那就想办法脱离苦海吧。我们仔细分析后发现,就是一个打包的问题,因此我们就可以使用Ant来完成这些琐碎的打包工作,下面就是《浅谈基于Mozilla ThunderBird的扩展开发》中使用的build.xml文件:
< project name ="helloworld" default ="createxpi" >
< delete file ="helloworld.xpi" />
< delete file ="helloworld.jar" />
< target name ="createjar" >
< zip destfile ="helloworld.jar" compress ="false" >
< fileset dir ="chrome" >
< include name ="content/**" />
</ fileset >
</ zip >
</ target >
< target name ="createxpi" depends ="createjar" >
< zip destfile ="helloworld.xpi" >
< zipfileset dir ="." includes ="helloworld.jar" prefix ="chrome" />
< zipfileset dir ="." includes ="chrome.manifest" />
< zipfileset dir ="." includes ="install.rdf" />
</ zip >
</ target >
</ project >
如果你的项目结构比这个要复杂的话,请根据自己的需求更改此文件再使用Ant编译就可以了。这种方法也是我目前使用最多的方法了,因为感觉控制起来比较灵活。
如果你喜欢集成化的开发工具,想让它为你做更多的事情的话,这里推荐一个工具:NetBean+foxbeans+foxfiles,后面两个插件是用于开发Mozilla平台插件的,下载地址为:
Foxbeans: http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=4209
Foxfiles: http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=4649
官方的入门文档:
http://wiki.netbeans.org/MozillaAddonDevelopment
不过不推荐用这个插件,试用了一下,并不是很好用。
再来谈下另一个问题,在做界面时,如何快速地获知目标程序中想overlay掉的控件的id以及它的属性呢?这在做插件开发时是一个比较重要的方面,因为你的插件的界面就是附着在目标程序(firefox,thunderbird或sunbird)上的。那么官方推荐的工具就够用了,这个插件就是DOM Inspector,它可以用来定位你所需要的控件。
Venkman是官方推荐的调试工具,但个人感觉还是不大好用。
另外几个调试的技巧是:1)可以试用”dump语句来显示调试信息,不过这需要进行一些配置工作。2)在代码中使用nsIConsoleService
将日志信息输出到JavaScript控制台中。
3)自己编译一个debug版本的firefox或thunderbird,并且在其源代码或你的C++ XPCOM组件中设置断点,进一步的信息可以参考Mozilla开发中心的文档。
除此以外,我们也可以自己开发一个日志工具类来记录感兴趣的信息,下面是我常用的一个日志组件类:
tBirdBiffUtility.classID = Components.ID( " {e07f8540-831f-11db-9fe1-0800200c9a66} " );
tBirdBiffUtility.contractID = " @phinecos.cnblogs.com/HelloWorld/utility;1 " ;
tBirdBiffUtility.classDescription = " UtilityService " ;
// 日志组件
function tBirdBiffUtility()
{
this.consoleService=CC["@mozilla.org/consoleservice;1"].getService(CI.nsIConsoleService);//控制台服务
this.logFile=null;//日志文件对象
this.logToFile=true;//是否开启日志功能,默认为开启,插件发布后,关闭它就是
}
tBirdBiffUtility.prototype =
{
initialize:function()
{//初始化
this.getLoggingPref();
},
closeLogFile:function()
{//关闭日志文件
if(this.logFile)
{
this.logFile.close(null);
}
},
openLogFile:function()
{//打开日志文件
this.closeLogFile();//关闭先前的日志文件
if(!this.logToFile)
{
return;
}
vartmpFile=CC["@mozilla.org/file/directory_service;1"]
.getService(CI.nsIProperties).get("TmpD",CI.nsILocalFile);
//构造日志文件名称
varfilename="HelloWorld";
if(this.isFirefox())
{
filename+="-firefox.log";
}
if(this.isSunbird())
{
filename+="-sunbird.log";
}
if(this.isThunderbird())
{
filename+="-thunderbird.log";
}
tmpFile.append(filename);
//创建不重名的日志文件
tmpFile.createUnique(CI.nsIFile.NORMAL_FILE_TYPE,0664);
this.logFile=CC["@mozilla.org/network/file-output-stream;1"]
.createInstance(CI.nsIFileOutputStream);
//write,create,truncate
this.logFile.init(tmpFile,0x02|0x08|0x20,0664,this.logFile.DEFAULT_REPLACEMENT_CHARACTER);
},
isFirefox:function()
{
if(this.getApplicationId()=="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
{
returntrue;
}
else
{
returnfalse;
}
},
isSunbird:function()
{
if(this.getApplicationId()=="{718e30fb-e89b-41dd-9da7-e25a45638b28}")
{
returntrue;
}
else
{
returnfalse;
}
},
isThunderbird:function()
{
if(this.getApplicationId()=="{3550f703-e582-4d05-9a08-453d09bdfdc6}")
{
returntrue;
}
else
{
returnfalse;
}
},
getLoggingPref:function()
{
//lookforanduseapreferenceifgiven
if(this.logToFile==true)
{//开启了日志功能
this.openLogFile();
this.logToConsole("tBirdBiffUtility.getLoggingPref","thunderbirdbiff.logToFilepreferencesettingis"+this.logToFile);
}
else
{//没有开启日志功能
this.logToConsole("tBirdBiffUtility.logToFile","Nothunderbirdbiff.logToFilepreferencesettingfound,usingFALSEdefault");
this.logToFile=false;
}
},
logError:function(source,message)
{//记录错误信息
varcallingFunction=source;
varerror=CC["@mozilla.org/scripterror;1"].createInstance(CI.nsIScriptError);
error.init(callingFunction+"():"+message,null,null,null,null,CI.nsIScriptError.errorFlag,null);
this.consoleService.logMessage(error);
this.log(source,"ERROR-"+message);
this.callingFunction=null;
this.error=null;
},
writeToLogFile:function(msg)
{//写入日志文件
if(this.logToFile)
{
this.logFile.write(msg+"/n",msg.length+1);
}
},
log:function(source,message)
{//日志功能
varmsg=source+"():/n/"+message;
try
{
this.writeToLogFile(msg);
}
catch(e)
{
varerrorMsg="Errortryingtowritelogfile:"+e+"--Openinganewfile/n";
this.logToConsole(errorMsg);
this.openLogFile();
if(this.logFile)
{
this.writeToLogFile(errorMsg);
this.writeToLogFile(msg);
}
errorMsg=null;
}
msg=null;
},
logToConsole:function(source,message)
{//写日志到控制台
varmsg=this.getAddonName()+"."+source+"():/n/"+message;
this.consoleService.logStringMessage(msg);
this.log(source,message);
msg=null;
},
getApplicationInfo:function()
{//获取目标程序信息
returnCC["@mozilla.org/xre/app-info;1"].getService(CI.nsIXULAppInfo);
},
getApplicationId:function()
{//获取目标程序id
if(!this.id)
{
this.id=this.getApplicationInfo().ID;
}
returnthis.id;
},
onObserve:function(subject,topic,data)
{
},
//nsISupports
QueryInterface:function(aIID)
{
if(aIID.equals(CI.nsISupports)||
aIID.equals(CI.nsIClassInfo)||
aIID.equals(CI.nsIObserver))
{
returnthis;
}
throwCR.NS_ERROR_NO_INTERFACE;
},
//nsIClassInfo
getInterfaces:function(aCount)
{
varifaces=newArray();
ifaces.push(CI.nsISupports);
ifaces.push(CI.nsIClassInfo);
ifaces.push(CI.nsIObserver);
aCount.value=ifaces.length;
returnifaces;
},
//nsIClassInfo
getHelperForLanguage:function(aLanguage){returnnull;},
getcontractID(){returntBirdBiffUtility.contractID;},
getclassID(){returntBirdBiffUtility.classID;},
getclassDescription(){returntBirdBiffUtility.classDescription;},
getimplementationLanguage(){returnCI.nsIProgrammingLanguage.JAVASCRIPT;},
getflags(){returnCI.nsIClassInfo.SINGLETON;},
//nsIObserver
observe:function(aSubject,aTopic,aData)
{
switch(aTopic)
{
case"xpcom-startup":
{
//thisisrunveryearly,rightafterXPCOMisinitialized,butbefore
//userprofileinformationisapplied.Registerourselvesasanobserver
//for'profile-after-change'and'quit-application'
//
varobsSvc=CC["@mozilla.org/observer-service;1"].getService(CI.nsIObserverService);
obsSvc.addObserver(this,"profile-after-change",false);
obsSvc.addObserver(this,"quit-application",false);
break;
}
case"profile-after-change":
{
//Thishappensafterprofilehasbeenloadedanduserpreferenceshavebeenread.
//startupcodehere
this.initialize();
break;
}
case"quit-application":
{
varobsSvc=CC["@mozilla.org/observer-service;1"].getService(CI.nsIObserverService);
obsSvc.removeObserver(this,"profile-after-change");
obsSvc.removeObserver(this,"quit-application");
break;
}
default:
{
this.onObserve(aSubject,aTopic,aData);
}
}
},
getwrappedJSObject(){returnthis;}
}
// ====================================================================================
// constructorsforobjectswewanttoXPCOMify
//
var gXpComObjects = [tBirdBiffUtility];
var gCatObserverName = tBirdBiffUtility.classDescription; // canbeanything
var gCatContractID = tBirdBiffUtility.contractID;
// **********
// genericregistrationcodebelow.UsefulURL:http://www.mozilla.org/projects/xpcom/book/cxc/html/weblock.html
// **********
function NSGetModule(compMgr,fileSpec)
{
gModule._catObserverName=gCatObserverName;
gModule._catContractId=gCatContractID;
for(variingXpComObjects)
gModule._xpComObjects[i]=newFactoryHolder(gXpComObjects[i]);
returngModule;
}
function FactoryHolder(aObj)
{
this.classID=aObj.classID;
this.contractID=aObj.contractID;
this.className=aObj.classDescription;
this.factory=
{
createInstance:function(aOuter,aIID)
{
if(aOuter)
throwCR.NS_ERROR_NO_AGGREGATION;
return(newthis.constructor).QueryInterface(aIID);
}
};
this.factory.constructor=aObj;
}
var gModule =
{
_xpComObjects:{},
_catObserverName:null,
_catContractId:null,
registerSelf:function(aComponentManager,aFileSpec,aLocation,aType)
{
aComponentManager.QueryInterface(CI.nsIComponentRegistrar);
for(varkeyinthis._xpComObjects)
{
varobj=this._xpComObjects[key];
aComponentManager.registerFactoryLocation(obj.classID,obj.className,
obj.contractID,aFileSpec,aLocation,aType);
}
varcatman=CC["@mozilla.org/categorymanager;1"].getService(CI.nsICategoryManager);
catman.addCategoryEntry("xpcom-startup",this._catObserverName,this._catContractId,true,true);
catman.addCategoryEntry("xpcom-shutdown",this._catObserverName,this._catContractId,true,true);
},
unregisterSelf:function(aCompMgr,aFileSpec,aLocation)
{
varcatman=CC["@mozilla.org/categorymanager;1"].getService(CI.nsICategoryManager);
catman.deleteCategoryEntry("xpcom-startup",this._catObserverName,true);
catman.deleteCategoryEntry("xpcom-shutdown",this._catObserverName,true);
aComponentManager.QueryInterface(CI.nsIComponentRegistrar);
for(varkeyinthis._xpComObjects)
{
varobj=this._xpComObjects[key];
aComponentManager.unregisterFactoryLocation(obj.classID,aFileSpec);
}
},
getClassObject:function(aComponentManager,aCID,aIID)
{
if(!aIID.equals(CI.nsIFactory))
throwCR.NS_ERROR_NOT_IMPLEMENTED;
for(varkeyinthis._xpComObjects)
{
if(aCID.equals(this._xpComObjects[key].classID))
returnthis._xpComObjects[key].factory;
}
throwCR.NS_ERROR_NO_INTERFACE;
},
canUnload:function(aComponentManager)
{
returntrue;
}
}