当前位置: 首页 > 工具软件 > Gecko-Java > 使用案例 >

gecko中的js扩展

秦涵涤
2023-12-01
1 创建js对象对应的idl文件
假设要扩展一个hiGecko对象,那么先创建一个idl文件,命名为nsIDOMHiGecko.idl,它的内容如下
#include "nsISupports.idl" 
[scriptable, uuid(9285aaa6-19fd-4fdb-8621-916f856c024f)]
interface nsIDOMHiGecko : nsISupports
{
    attribute long myval ; /*可读可写的属性*/
    boolean getIsOK();     /*一个接口*/
};

2 修改引用的Makefile
这里暂时先用gecko/dom/interfaces/base/Makefile.in
在XPIDLSRCS 变量上加上 nsIDOMHiGecko.idl

3 编译(这步可以先不做,只是可以先编译下,提前看下生成的接口是不是想要的,也可以放到 第6步 编译gecko 一起做)

编译成功后可以查看一下objdir-gecko-cosb2g/dist/include/nsIDOMHiGecko.h文件,里面定义了相应的C++类接口,在它最后的注释里还给出了如何写对应的c++实现



4 编写实现类
实现类有两种实现方式都可以,一种继承安全接口,可以控制哪些接口和属性可以在js里访问,一种不继承安全接口,不具有上面的功能

a)先介绍第二种,不继承安全接口
定义实现类文件nsDOMHiGecko.h, nsDOMHiGecko.cpp
*********nsDOMHiGecko.h的内容************
#ifndef nsDOMHiGecko_h_
#define nsDOMHiGecko_h_
#include "nsIDOMHiGecko.h" 

class nsDOMHiGecko : public nsIDOMHiGecko
{
public:
    NS_DECL_ISUPPORTS //声明com引用计数接口
    NS_DECL_NSIDOMHIGECKO // 声明hiGecko的接口
    nsDOMHiGecko();

protected:
    ~nsDOMHiGecko();

private:
    int32_t mValue;  // hiGecko的属性,用来保存idl里的myval
};
#endif

********nsDOMHiGecko.cpp的内容************
#include "nsDOMHiGecko.h" 
#include "nsDOMClassInfoID.h" 

nsDOMHiGecko::nsDOMHiGecko()
: mValue(0)
{
}

nsDOMHiGecko::~nsDOMHiGecko()
{
}

DOMCI_DATA(DOMHiGecko, nsDOMHiGecko)
NS_INTERFACE_MAP_BEGIN(nsDOMHiGecko)
  NS_INTERFACE_MAP_ENTRY(nsISupports) // 用于com接口查询
  NS_INTERFACE_MAP_ENTRY(nsIDOMHiGecko) // 用于nsIDOMHiGecko接口查询
  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMHiGecko) // 用于classinfo接口查询
NS_INTERFACE_MAP_END

NS_IMPL_ADDREF(nsDOMHiGecko)
NS_IMPL_RELEASE(nsDOMHiGecko)

NS_IMETHODIMP nsDOMHiGecko::GetIsOK(bool *_retval)
{
    *_retval = true;

    return NS_OK;
}

NS_IMETHODIMP nsDOMHiGecko::GetMyval(int32_t *aMyval)
{
    *aMyval = mValue;
    return NS_OK;
}

NS_IMETHODIMP nsDOMHiGecko::SetMyval(int32_t aMyval)
{
    mValue = aMyval;
    return NS_OK;
}

b)继承安全接口的实现方式
头文件
#ifndef nsDOMHiGecko_h_
#define nsDOMHiGecko_h_
#include "nsIDOMHiGecko.h" 
#include "nsISecurityCheckedComponent.h" 

class nsDOMHiGecko : public nsIDOMHiGecko
                      ,public nsISecurityCheckedComponent
{
public:
    NS_DECL_ISUPPORTS
    NS_DECL_NSIDOMHIGECKO
    NS_DECL_NSISECURITYCHECKEDCOMPONENT
    nsDOMHiGecko();

protected:
    ~nsDOMHiGecko();
private:
    int32_t mValue;  // hiGecko的属性,用来保存idl里的myval
};

#endif


cpp文件
#include "nsDOMHiGecko.h" 
#include "nsDOMClassInfoID.h" 

nsDOMHiGecko::nsDOMHiGecko()
{
}

nsDOMHiGecko::~nsDOMHiGecko()
{
}

NS_INTERFACE_MAP_BEGIN(nsDOMHiGecko)
  NS_INTERFACE_MAP_ENTRY(nsIDOMHiGecko)
  NS_INTERFACE_MAP_ENTRY(nsISecurityCheckedComponent)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMHiGecko)
NS_INTERFACE_MAP_END

NS_IMPL_ADDREF(nsDOMHiGecko)
NS_IMPL_RELEASE(nsDOMHiGecko)

NS_IMETHODIMP nsDOMHiGecko::GetIsOK(bool *_retval)
{
    *_retval = true;
    return NS_OK;
}

static char* cloneAllAccess()
{
  static const char allAccess[] = "AllAccess";
  return (char*)nsMemory::Clone(allAccess, sizeof(allAccess));
}

static char* cloneUniversalXPConnect()
{
  static const char universalXPConnect[] = "UniversalXPConnect";
  return (char*)nsMemory::Clone(universalXPConnect, sizeof(universalXPConnect));
}

NS_IMETHODIMP nsDOMHiGecko::GetMyval(int32_t *aMyval)
{
    *aMyval = mValue;
    return NS_OK;
}

NS_IMETHODIMP nsDOMHiGecko::SetMyval(int32_t aMyval)
{
    mValue = aMyval;
    return NS_OK;
}

NS_IMETHODIMP
nsDOMHiGecko::CanCreateWrapper(const nsIID * iid, char **_retval)
{
  *_retval = cloneAllAccess();
  return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

NS_IMETHODIMP //这个接口可以控制哪些方法可以在js里访问,这里用的是"allAccess"权限
nsDOMHiGecko::CanCallMethod(const nsIID * iid, const PRUnichar *methodName,
                                char **_retval)
{
  // OK if you're cool enough
  *_retval = cloneAllAccess();
  return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

NS_IMETHODIMP //这个接口可以控制哪些属性可以在js里读取,这里用的是"allAccess"权限
nsDOMHiGecko::CanGetProperty(const nsIID * iid,
                                 const PRUnichar *propertyName,
                                 char **_retval)
{
  // OK if you're cool enough
  *_retval = cloneAllAccess();
  return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

NS_IMETHODIMP //这个接口可以控制哪些属性可以在js里写入,这里用的是"allAccess"权限
nsDOMHiGecko::CanSetProperty(const nsIID * iid,
                                 const PRUnichar *propertyName,
                                 char **_retval)
{
  // OK if you're cool enough
  *_retval = cloneAllAccess();
  return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

5 在gecko里注册接口类和实现类

a. 在nsIDOMWindow.idl里定义hiGecko属性

readonly attribute nsIDOMHiGecko                      hiGecko;


b. 在类nsGlobalWindow定义对应c++成员方法
打开文件nsGlobaWindow.h给类nsGlobalWindow增加hiGecko成员对象
这里有两种引用计数方式,一种是com的引用计数nsComPtr,一般保存com形式的接口类,另一种是nsRefObj,直接保存实现类的引用计数
这两种方式在gecko里注册的方式不同,下面会做分别的介绍。


b.1 使用nsRefObj引用计数
在nsGlobalWindow类里增加成员
nsRefPtr<nsDOMHiGecko>   mHiGecko;

在nsGlobalWindow.h开始位置声明nsDOMHiGecko
class nsDOMHiGecko;

在nsGlobalWindow.cpp里引用nsDOMHiGecko
#include "nsDOMHiGecko.h" 

在nsGlobalWindow类的方法CleanUp里加上
mHiGecko = nullptr;

实现接口 GetHiGecko(这个接口的声明不用自己添加,会根据nsIDOMWindow.idl自动生成)
NS_IMETHODIMP
nsGlobalWindow::GetHiGecko(nsIDOMHiGecko * *aHiGecko)
{
  FORWARD_TO_OUTER(GetHiGecko, (aHiGecko), NS_ERROR_NOT_INITIALIZED);
  if (!mHiGecko) {
    mHiGecko = new nsDOMHiGecko();
    if (!mHiGecko) {
        return NS_ERROR_OUT_OF_MEMORY;
    }
  }

  NS_ADDREF(*aHiGecko = mHiGecko);

  return NS_OK;
}

b.2 使用nsCOMPtr引用计数, 不使用nsCOMPtr就跳过b.2,转向 c。
在nsGlobalWindow类里增加成员
nsCOMPtr<nsIDOMHiGecko>   mHiGecko; // 注意这里用的是接口类,而不是实现类

在nsGlobalWindow.h开始位置声明nsIDOMHiGecko
class nsIDOMHiGecko;

在nsGlobalWindow.cpp里引用nsDOMHiGecko // 这里是实现类
#include "nsDOMHiGecko.h" // 这里也是实现类


在nsGlobalWindow类的方法CleanUp里加上

mHiGecko = nullptr;

实现接口 GetHiGecko(这个接口的声明不用自己添加,会根据nsIDOMWindow.idl自动生成)
NS_IMETHODIMP
nsGlobalWindow::GetHiGecko(nsIDOMHiGecko * *aHiGecko)
{
  FORWARD_TO_OUTER(GetHiGecko, (aHiGecko), NS_ERROR_NOT_INITIALIZED);
  if (!mHiGecko) {
      nsresult rv;
      mHiGecko = do_CreateInstance(NS_DOMHIGECKO_CONTRACTID, &rv);
      NS_ENSURE_SUCCESS(rv, rv);
  }


  NS_ADDREF(*aHiGecko = mHiGecko);

  return NS_OK;
}

要想使用do_CreateInstance创建接口还要在几个地方注册一下
还有一种do_GetService的方法取得实例的方式,暂时感觉不需要。
这里暂时都注册到nsLayoutModule.cpp里

加上头文件
#include "nsDOMHiGecko.h" 

找到大量定义NS_DEFINE_NAMED_CID的地方加上NS_DEFINE_NAMED_CID(NS_DOMHIGECKO_CID);
找到static const mozilla::Module::CIDEntry kLayoutCIDs[],加上定义 { &kNS_DOMHIGECKO_CID, false, NULL, nsDOMHiGeckoConstructor },
找到static const mozilla::Module::ContractIDEntry kLayoutContracts[] 加上定义 { NS_DOMHIGECKO_CONTRACTID, &kNS_DOMHIGECKO_CID },
找到大量定义NS_GENERIC_FACTORY_CONSTRUCTOR的地方加上NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMHiGecko)
c. 为实现类定义uuid
在gecko/content/base/public/nsContentCID.h里定义实现类ID
// {5851d0eb-becd-4857-af8e-a78578c2c36f}
#define NS_DOMHIGECKO_CID \
{0x5851d0eb, 0xbecd, 0x4857, {0xaf, 0x8e, 0xa7, 0x85, 0x78, 0xc2, 0xc3, 0x6f}}
#define NS_DOMHIGECKO_CONTRACTID \
"@mozilla.org/higecko;1" 

d. 没有继承安全接口的情况下,要注册classinfo,如果是继承安全接口,这步不用做
d.1 打开gecko/dom/base/nsDOMClassInfoClasses.h,在最后一行添加
DOMCI_CLASS(DOMHiGecko)

d.2 打开gecko/dom/base/nsDOMClassInfo.cpp
找到static nsDOMClassInfoData sClassInfoData[]={, 这是个数组的定义
在最后一个成员加上(是不是最后一个成员没关系,但一定要保证和d.1里DOMCI_CLASS(DOMHiGecko)声明的顺序一致,
它是放到最后一个,所以这里也要放到最后一个,否则会打乱数组与类定义的映射,会崩溃)
NS_DEFINE_CLASSINFO_DATA(DOMHiGecko, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS)

d.3 找到 nsDOMClassInfo::Init()方法里的JSContext* cx = stack->GetSafeJSContext();
NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);

在这一段后面有DOM_CLASSINFO_MAP_BEGIN的注册实现,找个位置添加hiGecko的注册,(这个不用对应d.1和d.2的顺序)
DOM_CLASSINFO_MAP_BEGIN(DOMHiGecko, nsIDOMHiGecko)
  DOM_CLASSINFO_MAP_ENTRY(nsIDOMHiGecko)
DOM_CLASSINFO_MAP_END

e. 由于声明的hiGekcko属性是readonly,要加一些特殊处理
在nsDOMClassInfo.h里,nsDOMClassInfo类的声明里加上hiGecko id声明;
static jsid sDOMHiGecko_id;

在nsDOMClassInfo.h的IsReadonlyReplaceable方法里加上 ...id == sDOMHiGecko_id.....
在nsDOMClassInfo.cpp里加上jsid nsDOMClassInfo::sDOMHiGecko_id      = JSID_VOID; (给静态成员赋值)
在nsDOMClassInfo的方法DefineStaticJSVals里加上SET_JSID_TO_STRING(sDOMHiGecko_id,         cx, "hiGecko");
在nsDOMClassInfo的ShutDown方法里加上 sDOMHiGecko_id      = JSID_VOID;


f. 在Makefile里添加实现类

这里先加到gecko/dom/base/Makefile.in


6 编译gecko 编写测试页面
<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8">
    <meta name="viewport"  http-equiv="pragma">
    <title>Cut The Rope</title>
  </head>
  <body>
      <script>
           window.self.console.log("************ok************");
           window.hiGecko.myval = 34;
           window.console.log(window.hiGecko.myval);
           window.console.log(window.hiGecko.getIsOK());
      </script>
  </body>
</html>

运行webruntime,在终端执行adb logcat | grep -E "^E" 
如果能找到下面一段日志,证明扩展成功

E/GeckoConsole(16685): Content JS LOG at file:///sdcard/index.html:10 in anonymous: ************ok************
E/GeckoConsole(16685): Content JS LOG at file:///sdcard/index.html:12 in anonymous: 34
E/GeckoConsole(16685): Content JS LOG at file:///sdcard/index.html:13 in anonymous: true
 类似资料: