Bug 1178513 - Add <extapp> element and interfaces to be used by ACL. r=khuey, a=fabrice
☠☠ backed out by 58161f27c1d0 ☠ ☠
authorWilliam Chen <wchen@mozilla.com>
Thu, 02 Jul 2015 17:43:04 -0400
changeset 222157 ebcf221022c08e2405429b5ee6e66d75df132964
parent 222156 94d66febcf9c5699971420bbf51ad1f502aa2824
child 222158 58161f27c1d02e875e49bc06de0fdea6f66f670a
push id546
push userryanvm@gmail.com
push dateThu, 02 Jul 2015 22:03:55 +0000
treeherdermozilla-b2g34_v2_1@ebcf221022c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, fabrice
bugs1178513
milestone34.0
Bug 1178513 - Add <extapp> element and interfaces to be used by ACL. r=khuey, a=fabrice
content/base/src/nsGkAtomList.h
content/html/content/src/HTMLExtAppElement.cpp
content/html/content/src/HTMLExtAppElement.h
content/html/content/src/moz.build
content/html/content/src/nsGenericHTMLElement.h
content/html/content/test/mochitest.ini
content/html/content/test/test_extapp.html
dom/apps/PermissionsTable.jsm
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/ExternalAppEvent.webidl
dom/webidl/HTMLExtAppElement.webidl
dom/webidl/moz.build
editor/libeditor/nsHTMLEditUtils.cpp
parser/htmlparser/nsElementTable.cpp
parser/htmlparser/nsHTMLTagList.h
parser/htmlparser/nsHTMLTags.cpp
xpcom/system/moz.build
xpcom/system/nsIExternalApplication.idl
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -354,16 +354,17 @@ GK_ATOM(equalsize, "equalsize")
 GK_ATOM(error, "error")
 GK_ATOM(even, "even")
 GK_ATOM(event, "event")
 GK_ATOM(events, "events")
 GK_ATOM(excludeResultPrefixes, "exclude-result-prefixes")
 GK_ATOM(excludes, "excludes")
 GK_ATOM(expr, "expr")
 GK_ATOM(expectingSystemMessage, "expecting-system-message")
+GK_ATOM(extapp, "extapp")
 GK_ATOM(extends, "extends")
 GK_ATOM(extensionElementPrefixes, "extension-element-prefixes")
 GK_ATOM(face, "face")
 GK_ATOM(fallback, "fallback")
 GK_ATOM(_false, "false")
 GK_ATOM(farthest, "farthest")
 GK_ATOM(field, "field")
 GK_ATOM(fieldset, "fieldset")
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLExtAppElement.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "mozilla/dom/HTMLExtAppElement.h"
+#include "mozilla/dom/HTMLUnknownElement.h"
+#include "mozilla/dom/HTMLExtAppElementBinding.h"
+#include "mozilla/dom/ExternalAppEvent.h"
+
+#include "nsGkAtoms.h"
+#include "nsIAtom.h"
+#include "nsIPermissionManager.h"
+#include "nsStyleConsts.h"
+#include "nsRuleData.h"
+
+nsGenericHTMLElement*
+NS_NewHTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                        mozilla::dom::FromParser aFromParser) {
+  // Return HTMLUnknownElement if the document doesn't have the 'external-app' permission.
+  nsCOMPtr<nsIPermissionManager> permissionManager =
+    mozilla::services::GetPermissionManager();
+  nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
+  nsIPrincipal* principal = ni->GetDocument()->NodePrincipal();
+
+  already_AddRefed<mozilla::dom::NodeInfo> aarni = ni.forget();
+
+  if (!permissionManager) {
+    return new HTMLUnknownElement(aarni);
+  }
+
+  uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+  permissionManager->TestExactPermissionFromPrincipal(principal,
+                                                      "external-app",
+                                                      &perm);
+  if (perm != nsIPermissionManager::ALLOW_ACTION) {
+    return new HTMLUnknownElement(aarni);
+  }
+
+  return new HTMLExtAppElement(aarni);
+}
+
+namespace mozilla {
+namespace dom {
+
+HTMLExtAppElement::HTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+  : nsGenericHTMLElement(aNodeInfo)
+{
+  mCustomEventDispatch = new nsCustomEventDispatch(this);
+  mCustomPropertyBag = new nsCustomPropertyBag();
+
+  nsCOMPtr<nsIExternalApplication> app =
+    do_CreateInstance(NS_EXTERNALAPP_CONTRACTID);
+  if (app) {
+    nsresult rv = app->Init(OwnerDoc()->GetInnerWindow(), mCustomPropertyBag, mCustomEventDispatch);
+    if (NS_SUCCEEDED(rv)) {
+      mApp = app;
+    }
+  }
+}
+
+HTMLExtAppElement::~HTMLExtAppElement()
+{
+  mCustomEventDispatch->ClearEventTarget();
+}
+
+void
+HTMLExtAppElement::GetCustomProperty(const nsAString& aName, nsString& aReturn)
+{
+  mCustomPropertyBag->GetCustomProperty(aName, aReturn);
+}
+
+void
+HTMLExtAppElement::PostMessage(const nsAString& aMessage, ErrorResult& aRv)
+{
+  if (!mApp) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  aRv = mApp->PostMessage(aMessage);
+}
+
+NS_IMPL_ADDREF_INHERITED(HTMLExtAppElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLExtAppElement, Element)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLExtAppElement)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLExtAppElement,
+                                                nsGenericHTMLElement)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLExtAppElement,
+                                                  nsGenericHTMLElement)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+// QueryInterface implementation for HTMLExtAppElement
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLExtAppElement)
+NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+
+NS_IMPL_ELEMENT_CLONE(HTMLExtAppElement)
+
+JSObject*
+HTMLExtAppElement::WrapNode(JSContext *aCx)
+{
+  return HTMLExtAppElementBinding::Wrap(aCx, this);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+NS_IMPL_ISUPPORTS(nsCustomPropertyBag, nsICustomPropertyBag)
+
+nsCustomPropertyBag::nsCustomPropertyBag()
+{
+}
+
+nsCustomPropertyBag::~nsCustomPropertyBag()
+{
+}
+
+NS_IMETHODIMP
+nsCustomPropertyBag::SetProperty(const nsAString& aName, const nsAString& aValue)
+{
+  mBag.Put(nsString(aName), new nsString(aValue));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCustomPropertyBag::RemoveProperty(const nsAString& aName)
+{
+  mBag.Remove(nsString(aName));
+  return NS_OK;
+}
+
+void
+nsCustomPropertyBag::GetCustomProperty(const nsAString& aName, nsString& aReturn)
+{
+  nsString* value;
+  if (!mBag.Get(nsString(aName), &value)) {
+    aReturn.Truncate();
+    return;
+  }
+
+  MOZ_ASSERT(value);
+  aReturn.Assign(*value);
+}
+
+NS_IMPL_ISUPPORTS(nsCustomEventDispatch, nsICustomEventDispatch)
+
+nsCustomEventDispatch::nsCustomEventDispatch(mozilla::dom::EventTarget* aEventTarget)
+  : mEventTarget(aEventTarget)
+{
+  MOZ_ASSERT(mEventTarget);
+}
+
+void
+nsCustomEventDispatch::ClearEventTarget()
+{
+  mEventTarget = nullptr;
+}
+
+nsCustomEventDispatch::~nsCustomEventDispatch()
+{
+}
+
+NS_IMETHODIMP
+nsCustomEventDispatch::DispatchExternalEvent(const nsAString& value)
+{
+  if (!mEventTarget) {
+    return NS_OK;
+  }
+
+  mozilla::dom::ExternalAppEventInit init;
+  init.mData = value;
+
+  nsRefPtr<mozilla::dom::ExternalAppEvent> event =
+    mozilla::dom::ExternalAppEvent::Constructor(mEventTarget,
+                                                NS_LITERAL_STRING("externalappevent"),
+                                                init);
+
+  bool defaultActionEnabled;
+  return mEventTarget->DispatchEvent(event, &defaultActionEnabled);
+}
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLExtAppElement.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_HTMLExtAppElement_h
+#define mozilla_dom_HTMLExtAppElement_h
+
+#include "nsGenericHTMLElement.h"
+#include "nsIExternalApplication.h"
+
+class nsCustomEventDispatch;
+class nsCustomPropertyBag;
+
+namespace mozilla {
+namespace dom {
+
+class HTMLExtAppElement MOZ_FINAL : public nsGenericHTMLElement
+{
+public:
+  explicit HTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLExtAppElement,
+                                           nsGenericHTMLElement)
+
+  virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
+
+  void GetCustomProperty(const nsAString& aName, nsString& aReturn);
+  void PostMessage(const nsAString& aMessage, ErrorResult& aRv);
+
+protected:
+  virtual ~HTMLExtAppElement();
+
+  virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
+
+  nsRefPtr<nsCustomEventDispatch> mCustomEventDispatch;
+  nsRefPtr<nsCustomPropertyBag> mCustomPropertyBag;
+  nsCOMPtr<nsIExternalApplication> mApp;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+class nsCustomEventDispatch MOZ_FINAL : public nsICustomEventDispatch
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICUSTOMEVENTDISPATCH
+
+  explicit nsCustomEventDispatch(mozilla::dom::EventTarget* aEventTarget);
+  void ClearEventTarget();
+
+private:
+  ~nsCustomEventDispatch();
+
+  // Weak pointer, this object is owned by the event target.
+  mozilla::dom::EventTarget* mEventTarget;
+};
+
+class nsCustomPropertyBag MOZ_FINAL : public nsICustomPropertyBag
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICUSTOMPROPERTYBAG
+
+  nsCustomPropertyBag();
+  void GetCustomProperty(const nsAString& aName, nsString& aReturn);
+
+private:
+  ~nsCustomPropertyBag();
+
+  nsClassHashtable<nsStringHashKey, nsString> mBag;
+};
+
+#endif // mozilla_dom_HTMLExtAppElement_h
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -16,16 +16,17 @@ EXPORTS.mozilla.dom += [
     'HTMLAreaElement.h',
     'HTMLBodyElement.h',
     'HTMLBRElement.h',
     'HTMLButtonElement.h',
     'HTMLContentElement.h',
     'HTMLDataElement.h',
     'HTMLDataListElement.h',
     'HTMLDivElement.h',
+    'HTMLExtAppElement.h',
     'HTMLFieldSetElement.h',
     'HTMLFontElement.h',
     'HTMLFormControlsCollection.h',
     'HTMLFormElement.h',
     'HTMLFrameElement.h',
     'HTMLFrameSetElement.h',
     'HTMLHeadingElement.h',
     'HTMLHRElement.h',
@@ -88,16 +89,17 @@ UNIFIED_SOURCES += [
     'HTMLBRElement.cpp',
     'HTMLButtonElement.cpp',
     'HTMLCanvasElement.cpp',
     'HTMLContentElement.cpp',
     'HTMLDataElement.cpp',
     'HTMLDataListElement.cpp',
     'HTMLDivElement.cpp',
     'HTMLElement.cpp',
+    'HTMLExtAppElement.cpp',
     'HTMLFieldSetElement.cpp',
     'HTMLFontElement.cpp',
     'HTMLFormControlsCollection.cpp',
     'HTMLFormElement.cpp',
     'HTMLFrameElement.cpp',
     'HTMLFrameSetElement.cpp',
     'HTMLHeadingElement.cpp',
     'HTMLHRElement.cpp',
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1743,16 +1743,17 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(BR)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Body)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Button)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Canvas)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Content)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Mod)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Data)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(DataList)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Div)
+NS_DECLARE_NS_NEW_HTML_ELEMENT(ExtApp)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(FieldSet)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Font)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Form)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Frame)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(FrameSet)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(HR)
 NS_DECLARE_NS_NEW_HTML_ELEMENT_AS_SHARED(Head)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Heading)
--- a/content/html/content/test/mochitest.ini
+++ b/content/html/content/test/mochitest.ini
@@ -490,8 +490,9 @@ skip-if = buildapp == 'mulet' || buildap
 [test_ul_attributes_reflection.html]
 [test_undoManager.html]
 [test_video_wakelock.html]
 skip-if = toolkit == 'android' || (toolkit == 'gonk' && debug) #bug 871015, bug 881443
 [test_input_files_not_nsIFile.html]
 [test_ignoreuserfocus.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_fragment_form_pointer.html]
+[test_extapp.html]
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_extapp.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for extapp element being HTMLUnknownElement when permission is not available</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+  assert_true(document.createElement("extapp") instanceof HTMLUnknownElement);
+}, "extapp should be HTMLUnknownElement when external-app permission is not enabled.");
+</script>
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -494,16 +494,22 @@ this.PermissionsTable =  { geolocation: 
                              access: ["read", "write"],
                              additional: ["settings-api"]
                            },
                            "engineering-mode": {
                              app: DENY_ACTION,
                              trusted: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
+                           },
+                           "external-app": {
+                             app: DENY_ACTION,
+                             trusted: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
                            }
                          };
 
 /**
  * Append access modes to the permission name as suffixes.
  *   e.g. permission name 'contacts' with ['read', 'write'] =
  *   ['contacts-read', contacts-write']
  * @param string aPermName
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -371,16 +371,18 @@ var interfaceNamesInGlobalScope =
     "Event",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventSource",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventTarget",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "External", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "ExternalAppEvent", b2g: true, permission: ["external-app"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "File",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileReader",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "FMRadio", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -441,16 +443,18 @@ var interfaceNamesInGlobalScope =
     "HTMLDListElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLDocument",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLEmbedElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "HTMLExternalAppElement", b2g: true, permission: ["external-app"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFieldSetElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFontElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFormControlsCollection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFormElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ExternalAppEvent.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[Constructor(DOMString type, optional ExternalAppEventInit eventInitDict),
+ CheckPermissions="external-app"]
+interface ExternalAppEvent : Event
+{
+  readonly attribute DOMString data;
+};
+
+dictionary ExternalAppEventInit : EventInit
+{
+  DOMString data = "";
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/HTMLExtAppElement.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[CheckPermissions="external-app"]
+interface HTMLExtAppElement : HTMLElement {
+  // Gets the value of the property from a property bag
+  // that was provided to the external application.
+  DOMString getCustomProperty(DOMString name);
+
+  // Posts a message to the external application.
+  [Throws]
+  void postMessage(DOMString name);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -151,16 +151,17 @@ WEBIDL_FILES = [
     'HTMLDataElement.webidl',
     'HTMLDataListElement.webidl',
     'HTMLDirectoryElement.webidl',
     'HTMLDivElement.webidl',
     'HTMLDListElement.webidl',
     'HTMLDocument.webidl',
     'HTMLElement.webidl',
     'HTMLEmbedElement.webidl',
+    'HTMLExtAppElement.webidl',
     'HTMLFieldSetElement.webidl',
     'HTMLFontElement.webidl',
     'HTMLFormControlsCollection.webidl',
     'HTMLFormElement.webidl',
     'HTMLFrameElement.webidl',
     'HTMLFrameSetElement.webidl',
     'HTMLHeadElement.webidl',
     'HTMLHeadingElement.webidl',
@@ -654,16 +655,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'DataStoreChangeEvent.webidl',
     'DeviceLightEvent.webidl',
     'DeviceOrientationEvent.webidl',
     'DeviceProximityEvent.webidl',
     'DeviceStorageChangeEvent.webidl',
     'DOMTransactionEvent.webidl',
     'DownloadEvent.webidl',
     'ErrorEvent.webidl',
+    'ExternalAppEvent.webidl',
     'HashChangeEvent.webidl',
     'IccChangeEvent.webidl',
     'MediaStreamEvent.webidl',
     'MediaStreamTrackEvent.webidl',
     'MozApplicationEvent.webidl',
     'MozClirModeEvent.webidl',
     'MozContactChangeEvent.webidl',
     'MozEmergencyCbModeEvent.webidl',
--- a/editor/libeditor/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/nsHTMLEditUtils.cpp
@@ -673,16 +673,17 @@ static const nsElementInfo kElements[eHT
   ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(dfn, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(dir, true, false, GROUP_BLOCK, GROUP_LI),
   ELEM(div, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(dl, true, false, GROUP_BLOCK, GROUP_DL_CONTENT),
   ELEM(dt, true, true, GROUP_DL_CONTENT, GROUP_INLINE_ELEMENT),
   ELEM(em, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(embed, false, false, GROUP_NONE, GROUP_NONE),
+  ELEM(extapp, true, true, GROUP_NONE, GROUP_NONE),
   ELEM(fieldset, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(figcaption, true, false, GROUP_FIGCAPTION, GROUP_FLOW_ELEMENT),
   ELEM(figure, true, true, GROUP_BLOCK,
        GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION),
   ELEM(font, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(footer, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(form, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(frame, false, false, GROUP_FRAME, GROUP_NONE),
--- a/parser/htmlparser/nsElementTable.cpp
+++ b/parser/htmlparser/nsElementTable.cpp
@@ -167,16 +167,20 @@ const nsHTMLElement gHTMLElements[] = {
     /*tag*/         eHTMLTag_em,
     /*parent,leaf*/ kPhrase, false
   },
   {
     /*tag*/         eHTMLTag_embed,
     /*parent,leaf*/ kSpecial, true
   },
   {
+    /*tag*/         eHTMLTag_extapp,
+    /*parent,leaf*/ kNone, false
+  },
+  {
     /*tag*/         eHTMLTag_fieldset,
     /*parent,leaf*/ kBlock, false
   },
   {
     /*tag*/         eHTMLTag_figcaption,
     /*parent,leaf*/ kPhrase, false
   },
   {
--- a/parser/htmlparser/nsHTMLTagList.h
+++ b/parser/htmlparser/nsHTMLTagList.h
@@ -68,16 +68,17 @@ HTML_HTMLELEMENT_TAG(dd)
 HTML_TAG(del, Mod)
 HTML_HTMLELEMENT_TAG(dfn)
 HTML_TAG(dir, Shared)
 HTML_TAG(div, Div)
 HTML_TAG(dl, SharedList)
 HTML_HTMLELEMENT_TAG(dt)
 HTML_HTMLELEMENT_TAG(em)
 HTML_TAG(embed, SharedObject)
+HTML_TAG(extapp, ExtApp)
 HTML_TAG(fieldset, FieldSet)
 HTML_HTMLELEMENT_TAG(figcaption)
 HTML_HTMLELEMENT_TAG(figure)
 HTML_TAG(font, Font)
 HTML_HTMLELEMENT_TAG(footer)
 HTML_TAG(form, Form)
 HTML_TAG(frame, Frame)
 HTML_TAG(frameset, FrameSet)
--- a/parser/htmlparser/nsHTMLTags.cpp
+++ b/parser/htmlparser/nsHTMLTags.cpp
@@ -88,16 +88,18 @@ static const char16_t sHTMLTagUnicodeNam
 static const char16_t sHTMLTagUnicodeName_dl[] =
   {'d', 'l', '\0'};
 static const char16_t sHTMLTagUnicodeName_dt[] =
   {'d', 't', '\0'};
 static const char16_t sHTMLTagUnicodeName_em[] =
   {'e', 'm', '\0'};
 static const char16_t sHTMLTagUnicodeName_embed[] =
   {'e', 'm', 'b', 'e', 'd', '\0'};
+static const char16_t sHTMLTagUnicodeName_extapp[] =
+  {'e', 'x', 't', 'a', 'p', 'p', '\0'};
 static const char16_t sHTMLTagUnicodeName_fieldset[] =
   {'f', 'i', 'e', 'l', 'd', 's', 'e', 't', '\0'};
 static const char16_t sHTMLTagUnicodeName_figcaption[] =
   {'f', 'i', 'g', 'c', 'a', 'p', 't', 'i', 'o', 'n', '\0'};
 static const char16_t sHTMLTagUnicodeName_figure[] =
   {'f', 'i', 'g', 'u', 'r', 'e', '\0'};
 static const char16_t sHTMLTagUnicodeName_font[] =
   {'f', 'o', 'n', 't', '\0'};
--- a/xpcom/system/moz.build
+++ b/xpcom/system/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIBlocklistService.idl',
     'nsIDeviceSensors.idl',
+    'nsIExternalApplication.idl',
     'nsIGConfService.idl',
     'nsIGeolocationProvider.idl',
     'nsIGIOService.idl',
     'nsIGnomeVFSService.idl',
     'nsIGSettingsService.idl',
     'nsIHapticFeedback.idl',
     'nsIXULAppInfo.idl',
     'nsIXULRuntime.idl',
new file mode 100644
--- /dev/null
+++ b/xpcom/system/nsIExternalApplication.idl
@@ -0,0 +1,31 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+interface nsPIDOMWindow;
+
+[builtinclass, uuid(adf6b501-609a-4f54-ac16-39b54d6358b5)]
+interface nsICustomEventDispatch : nsISupports {
+  // Dispatches an event named "externalappevent" with the data
+  // in a property named "data" on the event.
+  void dispatchExternalEvent(in AString data);
+};
+
+[builtinclass, uuid(4d797f32-a24d-4cbc-b608-1eb0fc8e442b)]
+interface nsICustomPropertyBag : nsISupports {
+  void setProperty(in AString name, in AString value);
+  void removeProperty(in AString name);
+};
+
+[builtinclass, uuid(c100de94-e699-4941-b528-eaff1af5b15c)]
+interface nsIExternalApplication : nsISupports {
+  void init(in nsPIDOMWindow window, in nsICustomPropertyBag bag, in nsICustomEventDispatch callback);
+  void postMessage(in AString message);
+};
+
+%{C++
+#define NS_EXTERNALAPP_CONTRACTID "@mozilla.org/externalapp;1"
+#define NS_EXTERNALAPP_CID {0x8ec5dce2, 0x67e1, 0x49cd, {0xa0, 0x12, 0xf9, 0xfe, 0x32, 0x00, 0xd0, 0x8e}}
+%}