样式指南

与其他项目一样,我们也有一些代码规范需要遵守。对于整个 Marionette 项目,一些大致的规则是

  • 使您的代码可读且合理,不要试图耍小聪明。优先选择简单易懂的解决方案,而不是更复杂和陌生的语法。

  • 在进行实际更改时,将修复样式违规作为准备性清理步骤是好的,但在其他情况下,避免为了符合样式指南而进行无用的代码改动。

  • 代码是可变的,而不是一成不变的。签入的任何内容都不是神圣的,我们鼓励进行更改,使 remote/marionette 成为一个愉快的开发环境。

JavaScript

Marionette 使用 JavaScript 编写,并作为 Firefox 的一部分发布。我们可以访问当前正在开发的所有最新的 ECMAScript 功能,通常是在其正式发布之前,我们尝试在适当的时候使用新功能,尤其是在它们帮助我们摆脱旧的内部替换方案时。

作为运行时平台一部分的 JavaScript 代码的一个特性是,与普通的 Web 文档不同,我们与 Firefox 的其他部分共享一个全局状态。这意味着我们必须承担责任,并且不要不必要地泄露资源。

Gecko 中的 JS 代码被组织成模块,带有.js.sys.mjs 文件扩展名。根据您正在处理的 Gecko 区域,您可能会发现它们有不同的导出符号技术、不同的缩进和代码风格,以及不同的代码风格检查要求。

要将符号导出到其他 Marionette 模块,请记住将导出的符号分配给共享的全局this

const EXPORTED_SYMBOLS = ["PollPromise", "TimedPromise"];

在导入 Marionette 代码中的符号时,请尝试明确您需要什么

const { TimedPromise } = ChromeUtils.import(
  "chrome://remote/content/marionette/sync.js"
);

当重新定义名称时,我们更喜欢对象赋值简写,例如当您使用来自Components全局的功能时

const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;

当使用符号本身的名称时,可以省略赋值名称

const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer;

除了默认的Mozilla eslint 规则之外,我们还有我们自己的特殊规则,这些规则更严格并执行更多的安全检查。一些值得注意的示例是,我们不允许使用case语句贯穿,除非它们被明确地组合在一起

switch (x) {
  case "foo":
    doSomething();

  case "bar":  // <-- disallowed!
    doSomethingElse();
    break;

  case "baz":
  case "bah":  // <-- allowed (-:
    doCrazyThings();
}

我们不允许使用var,我们总是更喜欢使用letconst作为替代。请注意,const并不意味着变量是不可变的:只是它不能被重新赋值。我们要求所有行都以分号结尾,不允许创建普通的new Object(),要求变量名称使用驼峰命名法,并对未使用的变量发出警告。

出于纯粹的美观原因,我们使用两个空格缩进代码,包括 switch 语句的case,并将最大行长限制为 78 列。当您需要将语句换行到下一行时,第二行缩进四个空格,如下所示

throw new TypeError(pprint`Expected an element or WindowProxy, got: ${el}`);

这通常不是您需要深入思考的事情,因为它是由代码风格检查工具强制执行的。代码风格检查工具还具有自动模式,可以修复和格式化某些类型的样式违规。

如果您发现自己难以将一个长语句放在一行上,这通常表示该语句太长,应该拆分成多行。这也有助于使代码更易于阅读。将传递值赋予描述性变量名可以作为自我文档

let location = event.target.documentURI || event.target.location.href;
log.debug(`Received DOM event ${event.type} for ${location}`);

关于变量命名的主题,意见和编写代码的程序员一样多,但通常将函数的输入和输出参数描述得更详细(更长)是有帮助的,并让传递的内部值用更简洁的方式描述

/** Prettifies instance of Error and its stacktrace to a string. */
function stringify(error) {
  try {
    let s = error.toString();
    if ("stack" in error) {
      s += "\n" + error.stack;
    }
    return s;
  } catch (e) {
    return "<unprintable error>";
  }
}

在我们可以的情况下,我们尝试在事件处理程序或函数的参数中提取相关的对象属性

const responseListener = ({name, target, json, data}) => {  };

而不是

const responseListener = msg => {
  let name = msg.name;
  let target = msg.target;
  let json = msg.json;
  let data = msg.data;
  
};

所有源文件都应具有"use strict";作为第一个指令,以便在严格模式下解析该文件。

作为 Firefox 包的一部分发布的每个源代码文件还必须具有版权声明头,例如

    /* 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/. */

新的 xpcshell 测试文件不应该有许可证头,因为所有新的 Mozilla 测试都应该在公共领域中,以便它们可以轻松地与其他浏览器供应商共享。我们希望重新许可受MPL约束的现有测试,以便可以共享它们。我们非常欢迎您帮助进行版本控制考古学,以实现这一目标!

Contributing.md中概述了参与 Marionette 代码开发的实际细节,但通常在更改代码时无需重新构建 Firefox。对 remote/marionette/*.js 的任何更改都将在重新启动 Firefox 时生效。唯一的例外是 remote/components/Marionette.sys.mjs,它确实需要重新构建。

Python

待办事项

文档

我们将文档保存在树中,位于remote/doc/marionettetesting/geckodriver/doc下。文档的更新和微小更改理想情况下不应该像代码更改那样受到严格审查,以鼓励频繁更新,从而防止文档过时。为此,允许模块同行使用r=me进行文档更改。

使用 fmt(1) 或等效的编辑器特定机制(例如 Emacs 中的 Meta-Q)将段落格式化为最大宽度为 75 列,目标宽度约为 65 列。这等效于fmt -w 75 -g 65,这恰好是 BSD 和 macOS 上的默认设置。

我们努力记录 Marionette 组件的所有公共 API。这些包括GeckoDriver类上的公共函数(或命令实现),以及其他模块的所有导出符号。不需要为未导出的符号编写文档。

代码风格检查

Marionette 主要由 JavaScript(服务器)和 Python(客户端、测试套件、测试运行器)代码组成。我们使用mozlint对代码进行代码风格检查,该工具协调来自eslintruff的输出。

要使用合理的输出运行代码风格检查工具

% ./mach lint -funix remote/marionette

对于某些类型的样式违规,eslint 代码风格检查工具具有自动修复和格式化代码的模式。这对于保持空格和缩进规则特别有用

% ./mach eslint --fix remote/marionette

代码风格检查工具也作为 try 作业(简写ES)运行,这意味着任何样式违规都会自动阻止补丁落地(如果使用 Autoland)或导致您的更改集被回滚(如果直接落地到 mozilla-inbound)。

如果您使用 git(1),则可以在推送前启用自动代码风格检查(或提交前)钩子。这将在推送之前对已更改的文件运行代码风格检查工具,如果存在任何问题则中止。这对于避免由于愚蠢的代码风格检查问题导致 try 运行失败非常方便。