rdf.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. import os
  5. import xml.dom.minidom
  6. import StringIO
  7. RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  8. EM_NS = "http://www.mozilla.org/2004/em-rdf#"
  9. class RDF(object):
  10. def __str__(self):
  11. # real files have an .encoding attribute and use it when you
  12. # write() unicode into them: they read()/write() unicode and
  13. # put encoded bytes in the backend file. StringIO objects
  14. # read()/write() unicode and put unicode in the backing store,
  15. # so we must encode the output of getvalue() to get a
  16. # bytestring. (cStringIO objects are weirder: they effectively
  17. # have .encoding hardwired to "ascii" and put only bytes in
  18. # the backing store, so we can't use them here).
  19. #
  20. # The encoding= argument to dom.writexml() merely sets the XML header's
  21. # encoding= attribute. It still writes unencoded unicode to the output file,
  22. # so we have to encode it for real afterwards.
  23. #
  24. # Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=567660
  25. buf = StringIO.StringIO()
  26. self.dom.writexml(buf, encoding="utf-8")
  27. return buf.getvalue().encode('utf-8')
  28. class RDFUpdate(RDF):
  29. def __init__(self):
  30. impl = xml.dom.minidom.getDOMImplementation()
  31. self.dom = impl.createDocument(RDF_NS, "RDF", None)
  32. self.dom.documentElement.setAttribute("xmlns", RDF_NS)
  33. self.dom.documentElement.setAttribute("xmlns:em", EM_NS)
  34. def _make_node(self, name, value, parent):
  35. elem = self.dom.createElement(name)
  36. elem.appendChild(self.dom.createTextNode(value))
  37. parent.appendChild(elem)
  38. return elem
  39. def add(self, manifest, update_link):
  40. desc = self.dom.createElement("Description")
  41. desc.setAttribute(
  42. "about",
  43. "urn:mozilla:extension:%s" % manifest.get("em:id")
  44. )
  45. self.dom.documentElement.appendChild(desc)
  46. updates = self.dom.createElement("em:updates")
  47. desc.appendChild(updates)
  48. seq = self.dom.createElement("Seq")
  49. updates.appendChild(seq)
  50. li = self.dom.createElement("li")
  51. seq.appendChild(li)
  52. li_desc = self.dom.createElement("Description")
  53. li.appendChild(li_desc)
  54. self._make_node("em:version", manifest.get("em:version"),
  55. li_desc)
  56. apps = manifest.dom.documentElement.getElementsByTagName(
  57. "em:targetApplication"
  58. )
  59. for app in apps:
  60. target_app = self.dom.createElement("em:targetApplication")
  61. li_desc.appendChild(target_app)
  62. ta_desc = self.dom.createElement("Description")
  63. target_app.appendChild(ta_desc)
  64. for name in ["em:id", "em:minVersion", "em:maxVersion"]:
  65. elem = app.getElementsByTagName(name)[0]
  66. self._make_node(name, elem.firstChild.nodeValue, ta_desc)
  67. self._make_node("em:updateLink", update_link, ta_desc)
  68. class RDFManifest(RDF):
  69. def __init__(self, path):
  70. self.dom = xml.dom.minidom.parse(path)
  71. def set(self, property, value):
  72. elements = self.dom.documentElement.getElementsByTagName(property)
  73. if not elements:
  74. raise ValueError("Element with value not found: %s" % property)
  75. if not elements[0].firstChild:
  76. elements[0].appendChild(self.dom.createTextNode(value))
  77. else:
  78. elements[0].firstChild.nodeValue = value
  79. def get(self, property, default=None):
  80. elements = self.dom.documentElement.getElementsByTagName(property)
  81. if not elements:
  82. return default
  83. return elements[0].firstChild.nodeValue
  84. def remove(self, property):
  85. elements = self.dom.documentElement.getElementsByTagName(property)
  86. if not elements:
  87. return True
  88. else:
  89. for i in elements:
  90. i.parentNode.removeChild(i);
  91. return True;
  92. def gen_manifest(template_root_dir, target_cfg, jid,
  93. update_url=None, bootstrap=True, enable_mobile=False):
  94. install_rdf = os.path.join(template_root_dir, "install.rdf")
  95. manifest = RDFManifest(install_rdf)
  96. dom = manifest.dom
  97. manifest.set("em:id", jid)
  98. manifest.set("em:version",
  99. target_cfg.get('version', '1.0'))
  100. manifest.set("em:name",
  101. target_cfg.get('title', target_cfg.get('fullName', target_cfg['name'])))
  102. manifest.set("em:description",
  103. target_cfg.get("description", ""))
  104. manifest.set("em:creator",
  105. target_cfg.get("author", ""))
  106. manifest.set("em:bootstrap", str(bootstrap).lower())
  107. # XPIs remain packed by default, but package.json can override that. The
  108. # RDF format accepts "true" as True, anything else as False. We expect
  109. # booleans in the .json file, not strings.
  110. manifest.set("em:unpack", "true" if target_cfg.get("unpack") else "false")
  111. for translator in target_cfg.get("translators", [ ]):
  112. elem = dom.createElement("em:translator");
  113. elem.appendChild(dom.createTextNode(translator))
  114. dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem)
  115. for contributor in target_cfg.get("contributors", [ ]):
  116. elem = dom.createElement("em:contributor");
  117. elem.appendChild(dom.createTextNode(contributor))
  118. dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem)
  119. if update_url:
  120. manifest.set("em:updateURL", update_url)
  121. else:
  122. manifest.remove("em:updateURL")
  123. if target_cfg.get("preferences"):
  124. manifest.set("em:optionsType", "2")
  125. else:
  126. manifest.remove("em:optionsType")
  127. if enable_mobile:
  128. target_app = dom.createElement("em:targetApplication")
  129. dom.documentElement.getElementsByTagName("Description")[0].appendChild(target_app)
  130. ta_desc = dom.createElement("Description")
  131. target_app.appendChild(ta_desc)
  132. elem = dom.createElement("em:id")
  133. elem.appendChild(dom.createTextNode("{aa3c5121-dab2-40e2-81ca-7ea25febc110}"))
  134. ta_desc.appendChild(elem)
  135. elem = dom.createElement("em:minVersion")
  136. elem.appendChild(dom.createTextNode("19.0"))
  137. ta_desc.appendChild(elem)
  138. elem = dom.createElement("em:maxVersion")
  139. elem.appendChild(dom.createTextNode("22.0a1"))
  140. ta_desc.appendChild(elem)
  141. if target_cfg.get("homepage"):
  142. manifest.set("em:homepageURL", target_cfg.get("homepage"))
  143. else:
  144. manifest.remove("em:homepageURL")
  145. return manifest
  146. if __name__ == "__main__":
  147. print "Running smoke test."
  148. root = os.path.join(os.path.dirname(__file__), '../../app-extension')
  149. manifest = gen_manifest(root, {'name': 'test extension'},
  150. 'fakeid', 'http://foo.com/update.rdf')
  151. update = RDFUpdate()
  152. update.add(manifest, "https://foo.com/foo.xpi")
  153. exercise_str = str(manifest) + str(update)
  154. for tagname in ["em:targetApplication", "em:version", "em:id"]:
  155. if not len(update.dom.getElementsByTagName(tagname)):
  156. raise Exception("tag does not exist: %s" % tagname)
  157. if not update.dom.getElementsByTagName(tagname)[0].firstChild:
  158. raise Exception("tag has no children: %s" % tagname)
  159. print "Success!"