(FFOS Gecko & Gaia) OTA - Do real check

  虽然代码分析了很多,但是还没有真正做check的工作,下面就来找到真正的checker。

  代码位置:gecko/toolkit/mozapps/update/nsUpdateService.js。参考之前的(FFOS Gecko & Gaia) OTA - 代码模块总览,nsUpdateService.js中的Checker对象,实现了nsIUpdateChecker这个interface。下面就来分析Checker对象的实现。

 

1.  checkForUpdates函数:

  实现略长,在代码中添加注释分析。

checkForUpdates: function UC_checkForUpdates(listener, force) {
  LOG("Checker: checkForUpdates, force: " + force);
  if (!listener)
    throw Cr.NS_ERROR_NULL_POINTER;

// 通知'update-check-start'事件,监听者是谁呢?还记得上一篇分析的UpdatePrompt实现了nsIObserver吗? Services.obs.notifyObservers(
null, "update-check-start", null);
 // 这个函数很重要,它通过获取很多settings和preferences,还会通过libutils库,来获取很多系统参数,拼接成一个url,
// 这个url就是OTA server URL
var url = this.getUpdateURL(force); if (!url || (!this.enabled && !force)) return;
// 通过XHR发送GET请求,获取update信息
this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. createInstance(Ci.nsISupports); // This is here to let unit test code override XHR if (this._request.wrappedJSObject) { this._request = this._request.wrappedJSObject; } this._request.open("GET", url, true); var allowNonBuiltIn = !getPref("getBoolPref", PREF_APP_UPDATE_CERT_REQUIREBUILTIN, true); this._request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(allowNonBuiltIn); // Prevent the request from reading from the cache. this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; // Prevent the request from writing to the cache. this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; this._request.overrideMimeType("text/xml"); // The Cache-Control header is only interpreted by proxies and the // final destination. It does not help if a resource is already // cached locally. this._request.setRequestHeader("Cache-Control", "no-cache"); // HTTP/1.0 servers might not implement Cache-Control and // might only implement Pragma: no-cache this._request.setRequestHeader("Pragma", "no-cache"); var self = this;
// 设置XHR event listener
this._request.addEventListener("error", function(event) { self.onError(event); } ,false); this._request.addEventListener("load", function(event) { self.onLoad(event); }, false); LOG("Checker:checkForUpdates - sending request to: " + url); this._request.send(null); // 将传进来的listener保存下来,在XHR的event handler中回调 this._callback = listener; },

 

2. onLoad函数

  当request请求返回时,会回调onLoad函数

onLoad: function UC_onLoad(event) {
  LOG("Checker:onLoad - request completed downloading document");

  var prefs = Services.prefs;
  var certs = null;
  if (!getPref("getCharPref", PREF_APP_UPDATE_URL_OVERRIDE, null) &&
      getPref("getBoolPref", PREF_APP_UPDATE_CERT_CHECKATTRS, true)) {
    certs = gCertUtils.readCertPrefs(PREF_APP_UPDATE_CERTS_BRANCH);
  }

  try {
    // Analyze the resulting DOM and determine the set of updates.
  // 之前没有找到对于request response解析的部分,其实秘密就在于这里,Checker中并没有显示的定义_updates属性,而是定义了一个get _updates()函数。
// 这是js的特性,如果定义了get/set property()函数,就表示可以get/set这个以property命名的属性。
var updates = this._updates; LOG("Checker:onLoad - number of updates available: " + updates.length); var allowNonBuiltIn = !getPref("getBoolPref", PREF_APP_UPDATE_CERT_REQUIREBUILTIN, true); gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs); if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS)) Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS); if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS); // Tell the callback about the updates

this._callback.onCheckComplete(event.target, updates, updates.length); } catch (e) { LOG("Checker:onLoad - there was a problem checking for updates. " + "Exception: " + e); var request = event.target; var status = this._getChannelStatus(request); LOG("Checker:onLoad - request.status: " + status); var update = new Update(null); update.errorCode = status; update.statusText = getStatusTextFromCode(status, 404); if (this._isHttpStatusCode(status)) { update.errorCode = HTTP_ERROR_OFFSET + status; } if (e.result && e.result == Cr.NS_ERROR_ILLEGAL_VALUE) { update.errorCode = updates[0] ? CERT_ATTR_CHECK_FAILED_HAS_UPDATE : CERT_ATTR_CHECK_FAILED_NO_UPDATE; } this._callback.onError(request, update); } this._callback = null; this._request = null; },

 

3. get _updates()函数

  解析request response,根据mozilla的文档,这个response是一个update.xml文件,具体的格式请参考https://wiki.mozilla.org/Software_Update:updates.xml_Format

  通过解析update.xml文件,返回一个nsIUpdate数组,也就是Update对象的数组。Update的构造函数又会解析update.xml中的所有patch标签,每个patch标签会对应的生成一个nsIUpdatePatch,也就是UpdatePatch,也就是说Update对象里会包含1-多个UpdatePatch对象,并且Update对象里会保存一些其他的信息,比如是否为OS Update等,具体的解析过程请参考代码。


  /**
  * Returns an array of nsIUpdate objects discovered by the update check.
  * @throws if the XML document element node name is not updates.
  */

get _updates() {
  var updatesElement = this._request.responseXML.documentElement;
  if (!updatesElement) {
    LOG("Checker:_updates get - empty updates document?!");
    return [];
  }

  if (updatesElement.nodeName != "updates") {
    LOG("Checker:_updates get - unexpected node name!");
    throw new Error("Unexpected node name, expected: updates, got: " +
                    updatesElement.nodeName);
  }

  const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
  var updates = [];
  for (var i = 0; i < updatesElement.childNodes.length; ++i) {
    var updateElement = updatesElement.childNodes.item(i);
    if (updateElement.nodeType != ELEMENT_NODE ||
        updateElement.localName != "update")
      continue;

    updateElement.QueryInterface(Ci.nsIDOMElement);
    let update;
    try {
      update = new Update(updateElement);
    } catch (e) {
      LOG("Checker:_updates get - invalid <update/>, ignoring...");
      continue;
    }
    update.serviceURL = this.getUpdateURL(this._forced);
    update.channel = UpdateChannel.get();
    updates.push(update);
  }
  return updates;
},

 

posted @ 2015-08-05 10:45  coding4范儿  阅读(347)  评论(0编辑  收藏  举报