奇怪的壁虎行为(试图在js模块之间传递一个nsiDomWindows数组)



我正在编写一个引导火狐扩展。使用火狐开发者版 v36.2a

我有两个似乎相关的问题,我不明白。第一个,纯粹的烦恼:

my bootstrap Console.utils.import 一个加载我的应用程序的 js 文件(创建一个命名空间对象并将所有其他 js 文件作为命名空间的属性导入。所有其他文件都包含相同的命名空间文件,因此可以相互访问。它们甚至存储状态,并且所有工作方式都与节点缓存模块的方式非常相似,例如,似乎每个类只有一个对象在整个应用程序中共享。现在,这会发生:

我的命名空间文件执行以下操作:

namespace.console  = Components.utils.import( 'you know the one/console.jsm' )
namespace.Services = Components.utils.import( 'you know the one/Services.jsm')
namespace.Cu       = Components.utils

现在,在导入此文件的另一个文件中,我获得了命名空间对象,但namespace.Cu将未定义。 但是console.log( namespace )很好,并向我展示了Cu,我可以展开并查看其所有属性等等......所有其他事情(控制台、服务、我自己的类)都很好,但尝试组件中的 Cc、Ci 等 ->未定义的。

在我的应用程序的另一个地方,我有一个函数(在文件 A 中),它返回一个 nsiDomWindows 数组。文件 B 中的一个函数调用它,当它到达时,类似的故事:在控制台中一切看起来都很好,我可以查看带有 ChromeWidows 的数组。但它实际上不再是一个数组,而是对象类型,数组[0]未定义。如果我把两个类放在同一个文件中,它们就没问题了。

为了进一步混淆,我想我已经在另一个文件中使用了此方法,一切都很好:

// A.js
//
function A()
{
    b = new B()
    c = new C()
    let windows = b.windowList () // works fine, yields an actual valid array
                                  // with valid windows inside
    c.doSomething()               // broken, see below 
    c.doSomething( windows )      // passing it as a parameter 
                                  // doesn't help, still broken
}
// B.js
//
function B()
{
    this.windowList = function windowList()
    {
        let result = []
        // get open windows from nsiWindowMediator
        // foreach...
        // 
        result.push( nsiDomWindow )
        console.log( typeof result ) // Array -> it's completely valid here
        return result
    }
}

// C.js
//
function C()
{
    this.b = new B()

    this.doSomething = function doSomething( windows )
    {
        if( ! windows )
            windows = this.b.windowList()

        console.log( windows ) // in the console shows:
                               // Array[ ChromeWindow -> about:home ]
                               // I can inspect all it's properties, looks ok
        console.log( typeof windows )           // object
        console.log( windows.constructor.name ) // undefined
        console.log( windows[ 0 ]   )           // undefined
        // looping over the object properties shows that it does
        // have 1 property called '0' which points at undefined
        // note: there was only one open window in the array.
    }
}
// Note: the order in which I use Components.utils.import on these is B, C, A

我还想知道这是否与壁虎中的安全措施有关,但是在任何地方都看不到包装器对象,并且它们应该只保护内容代码免受chrome代码的影响(这都是chrome代码)。

正是

这种错误让我感到沮丧,因为我想不出一个合理的理由,为什么函数的返回值在调用的两端不应该是同一件事。

幸运的是,有一个

解决方法,我将让我的make文件连接我所有的js文件,并一劳永逸地使用Components.utils完成。似乎我必须使用的 Mozilla API 越少,我就会越快乐、更高效。

我也看到了来自 Mozilla 代码的错误。我刚刚在 Console.jsm 中遇到一个错误,说"shouldLog 不是一个函数"。此函数在该文件的上部明确定义。引发错误的行是正在返回的匿名函数。毫无疑问,这应该继承范围,但事实并非如此。可能是某些东西正在修改返回值,这是相关的。

我在 bugzilla 上提交了一个错误,并附加了一个示例插件,该插件演示了上述第一个问题。

更新

对不起,我混淆了两件事。我刚刚弄清楚在 Cu 不可用的第一个案例中发生了什么。我只是在将 Cu 添加到我的命名空间之前加载了出现问题的文件,并且由于我在全局范围内执行了 const { console, Services, Cu } = 命名空间,因此这实际上是在创建属性之前进行评估的。关于它的令人困惑的部分是控制台保留引用,而不是它向您展示的对象的副本(如果您问我,这是一个不幸的设计选择)在相关代码之前记录命名空间可以让您稍后查看其状态,我错过

了考虑。

这仍然不能解释一个函数如何向接收其他内容的接收函数返回一个完美的值,或者为什么在 Console.jsm 中声明的函数在继承其作用域的函数中似乎不再存在。

总而言之,第一个问题与其他问题没有任何关系。但是,我无法创建一个可重现的小插件来说明其他 2 个。如果我想办法,我会上传它。

顺便说一句:控制台的陷阱很容易看到(将以下内容放在javascript暂存器中):

let a = { some: "value" }
console.log( a )
delete a.some
// output: Object { }

出于调试目的(通常是主要目的),控制台有其限制,通常最好在调试器中设置断点或使用JSON.stringify

我想我已经将所有上述问题缩小到不正确地卸载。我有我没有删除的事件侦听器,当它们触发时,对象或范围可能会失效。

使用扩展自动安装程序在某种程度上使这些问题更加严重,因为当重新加载插件时,当它没有正确卸载时,会产生插件代码的不可靠状态。

shouldLog 不是一个函数,因为我认为我必须卸载我加载的所有模块 Components.utils.unload,所以我卸载了 Console.jsm。

在修复了这个问题并将所有入口点包装到我的软件中以 try-catch 块的形式之后,事情现在运行得更顺畅了。我现在很少需要在更新代码后卸载插件并重新启动 Firefox。

没有什么比几天有趣的调试更有趣的了......

我没有全部阅读,但是:我认为第一个问题是你导入错误了。

如果你想把它带到某个命名空间,从 Cu.import 导出的 var 你应该这样做:

Cu.import('resource://gre/modules/ctypes.jsm', namespace);

不需要 var blah = Cu.import

Console.jsm

和 Services.jsm 导出一个名为 te 相同事物的变量。

你甚至可以只做Cu.import('rsource://gre/modules/ctypes.jsm'),然后像ctypes.blah一样开始使用它

此外,要访问 Cu,请执行以下操作:

var {Cu: utils, Cr: results} = Components

接下来,我认为如果您在一个作用域中更改模块,则在对该作用域再次执行 Cu.import 之前,它不会在其他作用域中更改。

最新更新