Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Thu, 21 Mar 2019 12:41:13 +0200
changeset 465402 b2f1edb41241d3da6ded54edf38cca1d2d08325b
parent 465401 5cac2c92926e91b70e3858343efb12075433a42b (current diff)
parent 465400 38c2cb5142ebe1494b1f8720c9439bd8448f2255 (diff)
child 465403 b5c29835be635ba6e15af8eed918f4dbdd222c3c
child 465510 527e38b41c4e211630a8ff58783568a1774d1877
push id81047
push userncsoregi@mozilla.com
push dateThu, 21 Mar 2019 10:51:56 +0000
treeherderautoland@b5c29835be63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
b2f1edb41241 / 68.0a1 / 20190321104132 / files
nightly linux64
b2f1edb41241 / 68.0a1 / 20190321104132 / files
nightly mac
b2f1edb41241 / 68.0a1 / 20190321104132 / files
nightly win32
b2f1edb41241 / 68.0a1 / 20190321104132 / files
nightly win64
b2f1edb41241 / 68.0a1 / 20190321104132 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -38,17 +38,16 @@ support-files = test_bug332636.html^head
 skip-if = os == 'android'
 [test_bug372345.html]
 skip-if = toolkit == 'android'
 [test_bug404320.html]
 [test_bug408231.html]
 skip-if = toolkit == 'android'
 [test_bug410986.html]
 subsuite = clipboard
-skip-if = toolkit == 'android'
 [test_bug414526.html]
 [test_bug417418.html]
 skip-if = android_version == '18' # bug 1147989
 [test_bug426246.html]
 [test_bug430392.html]
 [test_bug432225.html]
 skip-if = toolkit == 'android'
 [test_bug439808.html]
@@ -56,36 +55,32 @@ skip-if = toolkit == 'android'
 [test_bug449243.html]
 [test_bug455992.html]
 [test_bug456244.html]
 [test_bug460740.html]
 [test_bug471319.html]
 [test_bug471722.html]
 [test_bug478725.html]
 subsuite = clipboard
-skip-if = toolkit == 'android'
 [test_bug480647.html]
 [test_bug480972.html]
 subsuite = clipboard
-skip-if = toolkit == 'android'
 [test_bug483651.html]
 [test_bug484181.html]
 skip-if = toolkit == 'android'
 [test_bug487524.html]
 [test_bug490879.html]
 subsuite = clipboard
 skip-if = toolkit == 'android' # bug 1299578
 [test_bug502673.html]
 [test_bug514156.html]
 [test_bug520189.html]
 subsuite = clipboard
-skip-if = toolkit == 'android'
 [test_bug525389.html]
 subsuite = clipboard
-skip-if = toolkit == 'android'
 [test_bug537046.html]
 [test_bug549262.html]
 skip-if = toolkit == 'android'
 [test_bug550434.html]
 skip-if = android_version == '18' # bug 1147989
 [test_bug551704.html]
 subsuite = clipboard
 [test_bug552782.html]
@@ -220,27 +215,25 @@ skip-if = toolkit == 'android' && !e10s 
 skip-if = os == 'android'
 [test_bug1162952.html]
 [test_bug1181130-1.html]
 [test_bug1181130-2.html]
 [test_bug1186799.html]
 [test_bug1230473.html]
 [test_bug1247483.html]
 subsuite = clipboard
-skip-if = toolkit == 'android'
 [test_bug1248128.html]
 [test_bug1250010.html]
 [test_bug1257363.html]
 [test_bug1248185.html]
 [test_bug1258085.html]
 [test_bug1268736.html]
 [test_bug1270235.html]
 [test_bug1306532.html]
 subsuite = clipboard
-skip-if = toolkit == 'android'
 [test_bug1310912.html]
 skip-if = toolkit == 'android' # bug 1315898
 [test_bug1314790.html]
 [test_bug1315065.html]
 [test_bug1316302.html]
 [test_bug1318312.html]
 [test_bug1328023.html]
 [test_bug1330796.html]
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/table-of-anyref.js
@@ -0,0 +1,11 @@
+// |jit-test| skip-if: !wasmReftypesEnabled()
+
+// Faulty prebarrier for tables.
+gczeal(4, 8);
+let ins = wasmEvalText(
+    `(module
+       (table (export "t") 10 anyref)
+       (func (export "set_anyref") (param i32) (param anyref)
+         (table.set (get_local 0) (get_local 1))))`);
+ins.exports.set_anyref(3, {});
+ins.exports.set_anyref(3, null);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11927,21 +11927,24 @@ class MWasmDerivedPointer : public MUnar
   }
 
   ALLOW_CLONE(MWasmDerivedPointer)
 };
 
 class MWasmLoadRef : public MUnaryInstruction, public NoTypePolicy::Data {
   AliasSet::Flag aliasSet_;
 
-  explicit MWasmLoadRef(MDefinition* valueAddr, AliasSet::Flag aliasSet)
+  explicit MWasmLoadRef(MDefinition* valueAddr, AliasSet::Flag aliasSet,
+                        bool isMovable = true)
       : MUnaryInstruction(classOpcode, valueAddr), aliasSet_(aliasSet) {
     MOZ_ASSERT(valueAddr->type() == MIRType::Pointer);
     setResultType(MIRType::RefOrNull);
-    setMovable();
+    if (isMovable) {
+      setMovable();
+    }
   }
 
  public:
   INSTRUCTION_HEADER(WasmLoadRef)
   TRIVIAL_NEW_WRAPPERS
 
   bool congruentTo(const MDefinition* ins) const override {
     return congruentIfOperandsEqual(ins);
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -852,28 +852,32 @@ Instance::tableInit(Instance* instance, 
                             JSMSG_WASM_OUT_OF_BOUNDS);
   return -1;
 }
 
 // The return convention for tableGet() is awkward but avoids a situation where
 // Ion code has to hold a value that may or may not be a pointer to GC'd
 // storage, or where Ion has to pass in a pointer to storage where a return
 // value can be written.
+//
+// Note carefully that the pointer that is returned may not be valid past
+// operations that change the size of the table or cause GC work; it is strictly
+// to be used to retrieve the return value.
 
 /* static */ void* /* nullptr to signal trap; pointer to table location
                       otherwise */
 Instance::tableGet(Instance* instance, uint32_t index, uint32_t tableIndex) {
   const Table& table = *instance->tables()[tableIndex];
   MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
   if (index >= table.length()) {
     JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                               JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
     return nullptr;
   }
-  return const_cast<void*>(table.getAnyRefLocForCompiledCode(index));
+  return const_cast<void*>(table.getShortlivedAnyRefLocForCompiledCode(index));
 }
 
 /* static */ uint32_t /* infallible */
 Instance::tableGrow(Instance* instance, uint32_t delta, void* initValue,
                     uint32_t tableIndex) {
   RootedAnyRef obj(TlsContext.get(), AnyRef::fromCompiledCode(initValue));
   Table& table = *instance->tables()[tableIndex];
   MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -724,18 +724,21 @@ class FunctionCompiler {
     curBlock_->end(MTest::New(alloc(), cond, failBlock, okBlock));
     failBlock->end(
         MWasmTrap::New(alloc(), wasm::Trap::ThrowReported, bytecodeOffset()));
     curBlock_ = okBlock;
     return true;
   }
 
   MDefinition* derefTableElementPointer(MDefinition* base) {
+    // Table element storage may be moved by GC operations, so reads from that
+    // storage are not movable.
     MWasmLoadRef* load =
-        MWasmLoadRef::New(alloc(), base, AliasSet::WasmTableElement);
+        MWasmLoadRef::New(alloc(), base, AliasSet::WasmTableElement,
+                          /*isMovable=*/ false);
     curBlock_->add(load);
     return load;
   }
 
   MDefinition* load(MDefinition* base, MemoryAccessDesc* access,
                     ValType result) {
     if (inDeadCode()) {
       return nullptr;
@@ -3155,18 +3158,18 @@ static bool EmitTableGet(FunctionCompile
   if (!f.passArg(tableIndexArg, ValType::I32, &args)) {
     return false;
   }
 
   if (!f.finishCall(&args)) {
     return false;
   }
 
-  // The return value here is either null, denoting an error, or a pointer to an
-  // unmovable location containing a possibly-null ref.
+  // The return value here is either null, denoting an error, or a short-lived
+  // pointer to a location containing a possibly-null ref.
   MDefinition* result;
   if (!f.builtinInstanceMethodCall(SASigTableGet, lineOrBytecode, args,
                                    &result)) {
     return false;
   }
   if (!f.checkPointerNullMeansFailedResult(result)) {
     return false;
   }
--- a/js/src/wasm/WasmTable.cpp
+++ b/js/src/wasm/WasmTable.cpp
@@ -143,19 +143,19 @@ const FunctionTableElem& Table::getAnyFu
 AnyRef Table::getAnyRef(uint32_t index) const {
   MOZ_ASSERT(!isFunction());
   // TODO/AnyRef-boxing: With boxed immediates and strings, the write barrier
   // is going to have to be more complicated.
   ASSERT_ANYREF_IS_JSOBJECT;
   return AnyRef::fromJSObject(objects_[index]);
 }
 
-const void* Table::getAnyRefLocForCompiledCode(uint32_t index) const {
+const void* Table::getShortlivedAnyRefLocForCompiledCode(uint32_t index) const {
   MOZ_ASSERT(!isFunction());
-  return objects_[index].address();
+  return const_cast<HeapPtr<JSObject*>&>(objects_[index]).unsafeUnbarrieredForTracing();
 }
 
 void Table::setAnyFunc(uint32_t index, void* code, const Instance* instance) {
   MOZ_ASSERT(isFunction());
 
   FunctionTableElem& elem = functions_[index];
   if (elem.tls) {
     JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
--- a/js/src/wasm/WasmTable.h
+++ b/js/src/wasm/WasmTable.h
@@ -33,17 +33,17 @@ namespace wasm {
 // pairs, where the instance must be traced.
 //
 // A table of AnyRef holds JSObject pointers, which must be traced.
 
 // TODO/AnyRef-boxing: With boxed immediates and strings, JSObject* is no longer
 // the most appropriate representation for Cell::anyref.
 STATIC_ASSERT_ANYREF_IS_JSOBJECT;
 
-typedef GCVector<JS::Heap<JSObject*>, 0, SystemAllocPolicy> TableAnyRefVector;
+typedef GCVector<HeapPtr<JSObject*>, 0, SystemAllocPolicy> TableAnyRefVector;
 
 class Table : public ShareableBase<Table> {
   using InstanceSet = JS::WeakCache<GCHashSet<
       ReadBarrieredWasmInstanceObject,
       MovableCellHasher<ReadBarrieredWasmInstanceObject>, SystemAllocPolicy>>;
   using UniqueAnyFuncArray = UniquePtr<FunctionTableElem[], JS::FreePolicy>;
 
   ReadBarrieredWasmTableObject maybeObject_;
@@ -82,17 +82,17 @@ class Table : public ShareableBase<Table
 
   // get/setAnyFunc is allowed only on table-of-funcref.
   // get/setAnyRef is allowed only on table-of-anyref.
   // setNull is allowed on either.
   const FunctionTableElem& getAnyFunc(uint32_t index) const;
   void setAnyFunc(uint32_t index, void* code, const Instance* instance);
 
   AnyRef getAnyRef(uint32_t index) const;
-  const void* getAnyRefLocForCompiledCode(uint32_t index) const;
+  const void* getShortlivedAnyRefLocForCompiledCode(uint32_t index) const;
   void setAnyRef(uint32_t index, AnyRef);
 
   void setNull(uint32_t index);
 
   // Copy entry from |srcTable| at |srcIndex| to this table at |dstIndex|.
   // Used by table.copy.
   void copy(const Table& srcTable, uint32_t dstIndex, uint32_t srcIndex);
 
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -661,19 +661,20 @@ static nsIFrame* GetFrameForChildrenOnly
     MOZ_ASSERT(aFrame->IsSVGOuterSVGAnonChildFrame(),
                "Where is the nsSVGOuterSVGFrame's anon child??");
   }
   MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer),
              "Children-only transforms only expected on SVG frames");
   return aFrame;
 }
 
-// Returns true if this function managed to successfully move a frame, and
-// false if it could not process the position change, and a reflow should
-// be performed instead.
+// This function tries to optimize a position style change by either
+// moving aFrame or ignoring the style change when it's safe to do so.
+// It returns true when that succeeds, otherwise it posts a reflow request
+// and returns false.
 static bool RecomputePosition(nsIFrame* aFrame) {
   // Don't process position changes on table frames, since we already handle
   // the dynamic position change on the table wrapper frame, and the
   // reflow-based fallback code path also ignores positions on inner table
   // frames.
   if (aFrame->IsTableFrame()) {
     return true;
   }
@@ -700,16 +701,22 @@ static bool RecomputePosition(nsIFrame* 
     nsIFrame* ph = aFrame->GetPlaceholderFrame();
     if (ph && ph->HasAnyStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
       StyleChangeReflow(aFrame, nsChangeHint_NeedReflow |
                                     nsChangeHint_ReflowChangesSizeOrPosition);
       return false;
     }
   }
 
+  // It's pointless to move around frames that have never been reflowed or
+  // are dirty (i.e. they will be reflowed).
+  if (aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY)) {
+    return true;
+  }
+
   aFrame->SchedulePaint();
 
   // For relative positioning, we can simply update the frame rect
   if (display->IsRelativelyPositionedStyle()) {
     // Move the frame
     if (display->mPosition == NS_STYLE_POSITION_STICKY) {
       // Update sticky positioning for an entire element at once, starting with
       // the first continuation or ib-split sibling.
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -448,17 +448,16 @@ static TimingFunction ToTimingFunction(
 
 static void SetAnimatable(nsCSSPropertyID aProperty,
                           const AnimationValue& aAnimationValue,
                           nsIFrame* aFrame, TransformReferenceBox& aRefBox,
                           layers::Animatable& aAnimatable) {
   MOZ_ASSERT(aFrame);
 
   if (aAnimationValue.IsNull()) {
-    printf_stderr("[Boris] set null animatable\n");
     aAnimatable = null_t();
     return;
   }
 
   switch (aProperty) {
     case eCSSProperty_background_color: {
       // We don't support color animation on the compositor yet so that we can
       // resolve currentColor at this moment.
--- a/layout/tools/layout-debug/src/nsLayoutDebuggingTools.cpp
+++ b/layout/tools/layout-debug/src/nsLayoutDebuggingTools.cpp
@@ -327,17 +327,17 @@ static void DumpContentRecur(nsIDocShell
 NS_IMETHODIMP
 nsLayoutDebuggingTools::DumpContent() {
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_NOT_INITIALIZED);
   DumpContentRecur(mDocShell, stdout);
   return NS_OK;
 }
 
 static void DumpFramesRecur(nsIDocShell* aDocShell, FILE* out) {
-#ifdef DEBUG
+#ifdef DEBUG_FRAME_DUMP
   fprintf(out, "webshell=%p \n", static_cast<void*>(aDocShell));
   nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
   if (shell) {
     nsIFrame* root = shell->GetRootFrame();
     if (root) {
       root->List(out);
     }
   } else {
--- a/mfbt/Vector.h
+++ b/mfbt/Vector.h
@@ -398,17 +398,24 @@ class MOZ_NON_PARAM Vector final : priva
   };
 
   template <size_t Dummy>
   struct CRAndStorage<0, Dummy> : CapacityAndReserved {
     explicit CRAndStorage(size_t aCapacity, size_t aReserved)
         : CapacityAndReserved(aCapacity, aReserved) {}
     CRAndStorage() = default;
 
-    T* storage() { return nullptr; }
+    T* storage() {
+      // If this returns |nullptr|, functions like |Vector::begin()| would too,
+      // breaking callers that pass a vector's elements as pointer/length to
+      // code that bounds its operation by length but (even just as a sanity
+      // check) always wants a non-null pointer.  Fake up an aligned, non-null
+      // pointer to support these callers.
+      return reinterpret_cast<T*>(sizeof(T));
+    }
   };
 
   CRAndStorage<kInlineCapacity, 0> mTail;
 
 #ifdef _MSC_VER
 #  pragma warning(pop)
 #endif  // _MSC_VER
 
--- a/mfbt/tests/TestVector.cpp
+++ b/mfbt/tests/TestVector.cpp
@@ -501,19 +501,81 @@ static_assert(sizeof(Vector<S, 0>) == si
 
 static_assert(sizeof(Vector<Incomplete, 0>) ==
                   sizeof(NoInlineStorageLayout<Incomplete>),
               "Vector of an incomplete class without inline storage shouldn't "
               "occupy dead space for that absence of storage");
 
 #endif  // DEBUG
 
+static void TestVectorBeginNonNull() {
+  // Vector::begin() should never return nullptr, to accommodate callers that
+  // (either for hygiene, or for semantic reasons) need a non-null pointer even
+  // for zero elements.
+
+  Vector<bool, 0> bvec0;
+  MOZ_RELEASE_ASSERT(bvec0.length() == 0);
+  MOZ_RELEASE_ASSERT(bvec0.begin() != nullptr);
+
+  Vector<bool, 1> bvec1;
+  MOZ_RELEASE_ASSERT(bvec1.length() == 0);
+  MOZ_RELEASE_ASSERT(bvec1.begin() != nullptr);
+
+  Vector<bool, 64> bvec64;
+  MOZ_RELEASE_ASSERT(bvec64.length() == 0);
+  MOZ_RELEASE_ASSERT(bvec64.begin() != nullptr);
+
+  Vector<int, 0> ivec0;
+  MOZ_RELEASE_ASSERT(ivec0.length() == 0);
+  MOZ_RELEASE_ASSERT(ivec0.begin() != nullptr);
+
+  Vector<int, 1> ivec1;
+  MOZ_RELEASE_ASSERT(ivec1.length() == 0);
+  MOZ_RELEASE_ASSERT(ivec1.begin() != nullptr);
+
+  Vector<int, 64> ivec64;
+  MOZ_RELEASE_ASSERT(ivec64.length() == 0);
+  MOZ_RELEASE_ASSERT(ivec64.begin() != nullptr);
+
+  Vector<long, 0> lvec0;
+  MOZ_RELEASE_ASSERT(lvec0.length() == 0);
+  MOZ_RELEASE_ASSERT(lvec0.begin() != nullptr);
+
+  Vector<long, 1> lvec1;
+  MOZ_RELEASE_ASSERT(lvec1.length() == 0);
+  MOZ_RELEASE_ASSERT(lvec1.begin() != nullptr);
+
+  Vector<long, 64> lvec64;
+  MOZ_RELEASE_ASSERT(lvec64.length() == 0);
+  MOZ_RELEASE_ASSERT(lvec64.begin() != nullptr);
+
+  // Vector<T, N> doesn't guarantee N inline elements -- the actual count is
+  // capped so that any Vector fits in a not-crazy amount of space -- so the
+  // code below won't overflow stacks or anything crazy.
+  struct VeryBig {
+    int array[16 * 1024 * 1024];
+  };
+
+  Vector<VeryBig, 0> vbvec0;
+  MOZ_RELEASE_ASSERT(vbvec0.length() == 0);
+  MOZ_RELEASE_ASSERT(vbvec0.begin() != nullptr);
+
+  Vector<VeryBig, 1> vbvec1;
+  MOZ_RELEASE_ASSERT(vbvec1.length() == 0);
+  MOZ_RELEASE_ASSERT(vbvec1.begin() != nullptr);
+
+  Vector<VeryBig, 64> vbvec64;
+  MOZ_RELEASE_ASSERT(vbvec64.length() == 0);
+  MOZ_RELEASE_ASSERT(vbvec64.begin() != nullptr);
+}
+
 int main() {
   VectorTesting::testReserved();
   VectorTesting::testConstRange();
   VectorTesting::testEmplaceBack();
   VectorTesting::testReverse();
   VectorTesting::testExtractRawBuffer();
   VectorTesting::testExtractOrCopyRawBuffer();
   VectorTesting::testReplaceRawBuffer();
   VectorTesting::testInsert();
   VectorTesting::testPodResizeToFit();
+  TestVectorBeginNonNull();
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/Clipboard.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/Clipboard.java
@@ -3,60 +3,123 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 
 import android.content.ClipboardManager;
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.text.TextUtils;
 
 public final class Clipboard {
+    private final static String HTML_MIME = "text/html";
+    private final static String UNICODE_MIME = "text/unicode";
     private final static String LOGTAG = "GeckoClipboard";
 
     private Clipboard() {
     }
 
+    /**
+     * Get the text on the primary clip on Android clipboard
+     *
+     * @param context application context.
+     * @return a plain text string of clipboard data.
+     */
+    public static String getText(final Context context) {
+        return getData(context, UNICODE_MIME);
+    }
+
+    /**
+     * Get the data on the primary clip on clipboard
+     *
+     * @param context application context
+     * @param mimeType the mime type we want. This supports text/html and text/unicode only.
+     * If other type, we do nothing.
+     * @return a string into clipboard.
+     */
     @WrapForJNI(calledFrom = "gecko")
-    public static String getText(final Context context) {
+    public static String getData(final Context context, final String mimeType) {
         final ClipboardManager cm = (ClipboardManager)
                 context.getSystemService(Context.CLIPBOARD_SERVICE);
         if (cm.hasPrimaryClip()) {
             ClipData clip = cm.getPrimaryClip();
-            if (clip != null && clip.getItemCount() > 0) {
-                ClipData.Item item = clip.getItemAt(0);
-                return item.coerceToText(context).toString();
+            if (clip == null || clip.getItemCount() == 0) {
+                return null;
+            }
+
+            ClipDescription description = clip.getDescription();
+            if (HTML_MIME.equals(mimeType) && description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
+                CharSequence data = clip.getItemAt(0).getHtmlText();
+                if (data == null) {
+                    return null;
+                }
+                return data.toString();
+            }
+            if (UNICODE_MIME.equals(mimeType)) {
+                return clip.getItemAt(0).coerceToText(context).toString();
             }
         }
         return null;
     }
 
+    /**
+     * Set plain text to clipboard
+     *
+     * @param context application context
+     * @param text a plain text to set to clipboard
+     */
     @WrapForJNI(calledFrom = "gecko")
     public static void setText(final Context context, final CharSequence text) {
+        setData(context, ClipData.newPlainText("text", text));
+    }
+
+    /**
+     * Store HTML to clipboard
+     *
+     * @param context application context
+     * @param text a plain text to set to clipboard
+     * @param html a html text to set to clipboard
+     */
+    @WrapForJNI(calledFrom = "gecko")
+    public static void setHTML(final Context context, final CharSequence text, final String htmlText) {
+        setData(context, ClipData.newHtmlText("html", text, htmlText));
+    }
+
+    /**
+     * Store {@link android.content.ClipData} to clipboard
+     *
+     * @param context application context
+     * @param clipData a {@link android.content.ClipData} to set to clipboard
+     */
+    private static void setData(final Context context, final ClipData clipData) {
         // In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager,
         // which is a subclass of android.text.ClipboardManager.
         final ClipboardManager cm = (ClipboardManager)
                 context.getSystemService(Context.CLIPBOARD_SERVICE);
         try {
-            cm.setPrimaryClip(ClipData.newPlainText("Text", text));
+            cm.setPrimaryClip(clipData);
         } catch (NullPointerException e) {
             // Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
             // a NullPointerException if Samsung's /data/clipboard directory is full.
             // Fortunately, the text is still successfully copied to the clipboard.
         }
     }
 
     /**
      * @return true if the clipboard is nonempty, false otherwise.
      */
     @WrapForJNI(calledFrom = "gecko")
-    public static boolean hasText(final Context context) {
-        return !TextUtils.isEmpty(getText(context));
+    public static boolean hasData(final Context context, final String mimeType) {
+        if (HTML_MIME.equals(mimeType) || UNICODE_MIME.equals(mimeType)) {
+            return !TextUtils.isEmpty(getData(context, mimeType));
+        }
+        return false;
     }
 
     /**
      * Deletes all text from the clipboard.
      */
     @WrapForJNI(calledFrom = "gecko")
     public static void clearText(final Context context) {
         setText(context, null);
--- a/mobile/android/modules/ActionBarHandler.jsm
+++ b/mobile/android/modules/ActionBarHandler.jsm
@@ -1,9 +1,10 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
 /* 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/. */
 "use strict";
 
 var EXPORTED_SYMBOLS = ["ActionBarHandler"];
 
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -437,28 +438,21 @@ var ActionBarHandler = {
             return false;
           }
           // Allow if selected text exists.
           return (ActionBarHandler._getSelectedText().length > 0);
         },
       },
 
       action: function(element, win) {
-        // First copy the selection text to the clipboard.
-        let selectedText = ActionBarHandler._getSelectedText();
-        let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
-          getService(Ci.nsIClipboardHelper);
-        clipboard.copyString(selectedText);
+        ActionBarHandler._getEditor(element, win).cut();
 
         let msg = Strings.browser.GetStringFromName("selectionHelper.textCopied");
         Snackbars.show(msg, Snackbars.LENGTH_LONG);
 
-        // Then cut the selection text.
-        ActionBarHandler._getSelection(element, win).deleteFromDocument();
-
         ActionBarHandler._uninit();
         UITelemetry.addEvent("action.1", "actionbar", null, "cut");
       },
     },
 
     COPY: {
       id: "copy_action",
       label: () => Strings.browser.GetStringFromName("contextmenu.copy"),
@@ -475,20 +469,17 @@ var ActionBarHandler = {
             return false;
           }
           // Allow if selected text exists.
           return (ActionBarHandler._getSelectedText().length > 0);
         },
       },
 
       action: function(element, win) {
-        let selectedText = ActionBarHandler._getSelectedText();
-        let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
-          getService(Ci.nsIClipboardHelper);
-        clipboard.copyString(selectedText);
+        ActionBarHandler._getEditor(element, win).copy();
 
         let msg = Strings.browser.GetStringFromName("selectionHelper.textCopied");
         Snackbars.show(msg, Snackbars.LENGTH_LONG);
 
         ActionBarHandler._uninit();
         UITelemetry.addEvent("action.1", "actionbar", null, "copy");
       },
     },
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -537,119 +537,131 @@ Result NSSCertDBTrustDomain::CheckRevoca
 
     // Nothing to do if we don't have an OCSP responder URI for the cert; just
     // assume it is good. Note that this is the confusing, but intended,
     // interpretation of "strict" revocation checking in the face of a
     // certificate that lacks an OCSP responder URI.
     return Success;
   }
 
-  // Only request a response if we didn't have a cached indication of failure
-  // (don't keep requesting responses from a failing server).
-  bool attemptedRequest;
-  Vector<uint8_t> ocspResponse;
-  Input response;
   if (cachedResponseResult == Success ||
       cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
       cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
-    uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
-    size_t ocspRequestLength;
-    rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
-                                  ocspRequestLength);
-    if (rv != Success) {
-      return rv;
-    }
-    Vector<uint8_t> ocspRequest;
-    if (!ocspRequest.append(ocspRequestBytes, ocspRequestLength)) {
-      return Result::FATAL_ERROR_NO_MEMORY;
-    }
-    Result tempRV =
-        DoOCSPRequest(aiaLocation, mOriginAttributes, std::move(ocspRequest),
-                      GetOCSPTimeout(), ocspResponse);
-    if (tempRV != Success) {
-      rv = tempRV;
-    } else if (response.Init(ocspResponse.begin(), ocspResponse.length()) !=
-               Success) {
-      rv = Result::ERROR_OCSP_MALFORMED_RESPONSE;  // too big
-    }
-    attemptedRequest = true;
-  } else {
-    rv = cachedResponseResult;
-    attemptedRequest = false;
+    // Only send a request to, and process a response from, the server if we
+    // didn't have a cached indication of failure.  Also, ddon't keep requesting
+    // responses from a failing server.
+    return SynchronousCheckRevocationWithServer(
+        certID, aiaLocation, time, maxOCSPLifetimeInDays, cachedResponseResult,
+        stapledOCSPResponseResult);
   }
 
-  if (response.GetLength() == 0) {
-    Result error = rv;
-    if (attemptedRequest) {
-      Time timeout(time);
-      if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
-        return Result::FATAL_ERROR_LIBRARY_FAILURE;  // integer overflow
-      }
-      rv = mOCSPCache.Put(certID, mOriginAttributes, error, time, timeout);
-      if (rv != Success) {
-        return rv;
-      }
-    }
-    if (mOCSPFetching != FetchOCSPForDVSoftFail) {
-      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
-              ("NSSCertDBTrustDomain: returning SECFailure after "
-               "OCSP request failure"));
-      return error;
-    }
-    if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
-      MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
-              ("NSSCertDBTrustDomain: returning SECFailure from cached "
-               "response after OCSP request failure"));
-      return cachedResponseResult;
-    }
-    if (stapledOCSPResponseResult != Success) {
-      MOZ_LOG(
-          gCertVerifierLog, LogLevel::Debug,
-          ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
-           "stapled response after OCSP request failure"));
-      return stapledOCSPResponseResult;
+  return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
+                           cachedResponseResult);
+}
+
+Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
+    const CertID& certID, const nsCString& aiaLocation, Time time,
+    uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult,
+    const Result stapledOCSPResponseResult) {
+  uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH];
+  size_t ocspRequestLength;
+
+  Result rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes,
+                                       ocspRequestLength);
+  if (rv != Success) {
+    return rv;
+  }
+
+  Vector<uint8_t> ocspResponse;
+  Input response;
+  rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes,
+                     ocspRequestLength, GetOCSPTimeout(), ocspResponse);
+  if (rv == Success &&
+      response.Init(ocspResponse.begin(), ocspResponse.length()) != Success) {
+    rv = Result::ERROR_OCSP_MALFORMED_RESPONSE;  // too big
+  }
+
+  if (rv != Success) {
+    Time timeout(time);
+    if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
+      return Result::FATAL_ERROR_LIBRARY_FAILURE;  // integer overflow
     }
 
-    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
-            ("NSSCertDBTrustDomain: returning SECSuccess after "
-             "OCSP request failure"));
-    return Success;  // Soft fail -> success :(
+    Result cacheRV =
+        mOCSPCache.Put(certID, mOriginAttributes, rv, time, timeout);
+    if (cacheRV != Success) {
+      return cacheRV;
+    }
+
+    return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
+                             rv);
   }
 
   // If the response from the network has expired but indicates a revoked
   // or unknown certificate, PR_GetError() will return the appropriate error.
   // We actually ignore expired here.
   bool expired;
   rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
                                               maxOCSPLifetimeInDays, response,
                                               ResponseIsFromNetwork, expired);
   if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
-    MOZ_LOG(
-        gCertVerifierLog, LogLevel::Debug,
-        ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
+    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+            ("NSSCertDBTrustDomain: returning after "
+             "VerifyEncodedOCSPResponse"));
     return rv;
   }
 
   if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
       rv == Result::ERROR_REVOKED_CERTIFICATE) {
     return rv;
   }
+
   if (stapledOCSPResponseResult != Success) {
     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
             ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
              "stapled response after OCSP request verification failure"));
     return stapledOCSPResponseResult;
   }
 
   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
           ("NSSCertDBTrustDomain: end of CheckRevocation"));
 
   return Success;  // Soft fail -> success :(
 }
 
+Result NSSCertDBTrustDomain::HandleOCSPFailure(
+    const Result cachedResponseResult, const Result stapledOCSPResponseResult,
+    const Result error) {
+  if (mOCSPFetching != FetchOCSPForDVSoftFail) {
+    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+            ("NSSCertDBTrustDomain: returning SECFailure after OCSP request "
+             "failure"));
+    return error;
+  }
+
+  if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
+    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+            ("NSSCertDBTrustDomain: returning SECFailure from cached response "
+             "after OCSP request failure"));
+    return cachedResponseResult;
+  }
+
+  if (stapledOCSPResponseResult != Success) {
+    MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+            ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid "
+             "stapled response after OCSP request failure"));
+    return stapledOCSPResponseResult;
+  }
+
+  MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+          ("NSSCertDBTrustDomain: returning SECSuccess after OCSP request "
+           "failure"));
+  return Success;  // Soft fail -> success :(
+}
+
 Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
     const CertID& certID, Time time, uint16_t maxLifetimeInDays,
     Input encodedResponse, EncodedResponseSource responseSource,
     /*out*/ bool& expired) {
   Time thisUpdate(Time::uninitialized);
   Time validThrough(Time::uninitialized);
 
   // We use a try and fallback approach which first mandates good signature
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -197,16 +197,25 @@ class NSSCertDBTrustDomain : public mozi
     ResponseWasStapled = 2
   };
   Result VerifyAndMaybeCacheEncodedOCSPResponse(
       const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
       uint16_t maxLifetimeInDays, mozilla::pkix::Input encodedResponse,
       EncodedResponseSource responseSource, /*out*/ bool& expired);
   TimeDuration GetOCSPTimeout() const;
 
+  Result SynchronousCheckRevocationWithServer(
+      const mozilla::pkix::CertID& certID, const nsCString& aiaLocation,
+      mozilla::pkix::Time time, uint16_t maxOCSPLifetimeInDays,
+      const Result cachedResponseResult,
+      const Result stapledOCSPResponseResult);
+  Result HandleOCSPFailure(const Result cachedResponseResult,
+                           const Result stapledOCSPResponseResult,
+                           const Result error);
+
   const SECTrustType mCertDBTrustType;
   const OCSPFetching mOCSPFetching;
   OCSPCache& mOCSPCache;  // non-owning!
   void* mPinArg;          // non-owning!
   const mozilla::TimeDuration mOCSPTimeoutSoft;
   const mozilla::TimeDuration mOCSPTimeoutHard;
   const uint32_t mCertShortLifetimeInDays;
   CertVerifier::PinningMode mPinningMode;
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -9,16 +9,17 @@
 #include "PSMRunnable.h"
 #include "ScopedNSSTypes.h"
 #include "SharedCertVerifier.h"
 #include "SharedSSLState.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/Span.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 #include "nsContentUtils.h"
 #include "nsICertOverrideService.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIPrompt.h"
 #include "nsIProtocolProxyService.h"
 #include "nsISupportsPriority.h"
@@ -60,19 +61,20 @@ namespace {
 const uint32_t POSSIBLE_VERSION_DOWNGRADE = 4;
 const uint32_t POSSIBLE_CIPHER_SUITE_DOWNGRADE = 2;
 const uint32_t KEA_NOT_SUPPORTED = 1;
 
 }  // namespace
 
 class OCSPRequest final : public nsIStreamLoaderObserver, public nsIRunnable {
  public:
-  OCSPRequest(const nsCString& aiaLocation,
+  OCSPRequest(const nsACString& aiaLocation,
               const OriginAttributes& originAttributes,
-              Vector<uint8_t>&& ocspRequest, TimeDuration timeout);
+              const uint8_t (&ocspRequest)[OCSP_REQUEST_MAX_LENGTH],
+              size_t ocspRequestLength, TimeDuration timeout);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISTREAMLOADEROBSERVER
   NS_DECL_NSIRUNNABLE
 
   nsresult DispatchToMainThreadAndWait();
   nsresult GetResponse(/*out*/ Vector<uint8_t>& response);
 
@@ -99,40 +101,43 @@ class OCSPRequest final : public nsIStre
   // cancelled. This is how we know the closure in OnTimeout is valid. If the
   // timer fires before OnStreamComplete runs, it should be safe to not cancel
   // the request because necko has a strong reference to it.
   Monitor mMonitor;
   bool mNotifiedDone;
   nsCOMPtr<nsIStreamLoader> mLoader;
   const nsCString mAIALocation;
   const OriginAttributes mOriginAttributes;
-  const Vector<uint8_t> mPOSTData;
+  const mozilla::Span<const char> mPOSTData;
   const TimeDuration mTimeout;
   nsCOMPtr<nsITimer> mTimeoutTimer;
   TimeStamp mStartTime;
   nsresult mResponseResult;
   Vector<uint8_t> mResponseBytes;
 };
 
 NS_IMPL_ISUPPORTS(OCSPRequest, nsIStreamLoaderObserver, nsIRunnable)
 
-OCSPRequest::OCSPRequest(const nsCString& aiaLocation,
+OCSPRequest::OCSPRequest(const nsACString& aiaLocation,
                          const OriginAttributes& originAttributes,
-                         Vector<uint8_t>&& ocspRequest, TimeDuration timeout)
+                         const uint8_t (&ocspRequest)[OCSP_REQUEST_MAX_LENGTH],
+                         size_t ocspRequestLength, TimeDuration timeout)
     : mMonitor("OCSPRequest.mMonitor"),
       mNotifiedDone(false),
       mLoader(nullptr),
       mAIALocation(aiaLocation),
       mOriginAttributes(originAttributes),
-      mPOSTData(std::move(ocspRequest)),
+      mPOSTData(reinterpret_cast<const char*>(ocspRequest), ocspRequestLength),
       mTimeout(timeout),
       mTimeoutTimer(nullptr),
       mStartTime(),
       mResponseResult(NS_ERROR_FAILURE),
-      mResponseBytes() {}
+      mResponseBytes() {
+  MOZ_ASSERT(ocspRequestLength <= OCSP_REQUEST_MAX_LENGTH);
+}
 
 nsresult OCSPRequest::DispatchToMainThreadAndWait() {
   MOZ_ASSERT(!NS_IsMainThread());
   if (NS_IsMainThread()) {
     return NS_ERROR_FAILURE;
   }
 
   MonitorAutoLock lock(mMonitor);
@@ -274,20 +279,17 @@ OCSPRequest::Run() {
     nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
     rv = loadInfo->SetOriginAttributes(attrs);
     if (NS_FAILED(rv)) {
       return NotifyDone(rv, lock);
     }
   }
 
   nsCOMPtr<nsIInputStream> uploadStream;
-  rv = NS_NewByteInputStream(
-      getter_AddRefs(uploadStream),
-      MakeSpan(reinterpret_cast<const char*>(mPOSTData.begin()),
-               mPOSTData.length()));
+  rv = NS_NewByteInputStream(getter_AddRefs(uploadStream), mPOSTData);
   if (NS_FAILED(rv)) {
     return NotifyDone(rv, lock);
   }
   nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(channel));
   if (!uploadChannel) {
     return NotifyDone(NS_ERROR_FAILURE, lock);
   }
   rv = uploadChannel->SetUploadStream(uploadStream, OCSP_REQUEST_MIME_TYPE, -1);
@@ -420,26 +422,29 @@ void OCSPRequest::OnTimeout(nsITimer* ti
   // (i.e. OnStreamComplete ran), the timer would have been cancelled in
   // NotifyDone.
   OCSPRequest* self = static_cast<OCSPRequest*>(closure);
   MonitorAutoLock lock(self->mMonitor);
   self->mTimeoutTimer = nullptr;
   self->NotifyDone(NS_ERROR_NET_TIMEOUT, lock);
 }
 
-mozilla::pkix::Result DoOCSPRequest(const nsCString& aiaLocation,
-                                    const OriginAttributes& originAttributes,
-                                    Vector<uint8_t>&& ocspRequest,
-                                    TimeDuration timeout,
-                                    /*out*/ Vector<uint8_t>& result) {
+mozilla::pkix::Result DoOCSPRequest(
+    const nsCString& aiaLocation, const OriginAttributes& originAttributes,
+    uint8_t (&ocspRequest)[OCSP_REQUEST_MAX_LENGTH], size_t ocspRequestLength,
+    TimeDuration timeout, /*out*/ Vector<uint8_t>& result) {
   MOZ_ASSERT(!NS_IsMainThread());
   if (NS_IsMainThread()) {
     return mozilla::pkix::Result::ERROR_OCSP_UNKNOWN_CERT;
   }
 
+  if (ocspRequestLength > OCSP_REQUEST_MAX_LENGTH) {
+    return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+
   result.clear();
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
           ("DoOCSPRequest to '%s'", aiaLocation.get()));
 
   nsCOMPtr<nsIEventTarget> sts =
       do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
   MOZ_ASSERT(sts);
   if (!sts) {
@@ -450,18 +455,18 @@ mozilla::pkix::Result DoOCSPRequest(cons
   if (NS_FAILED(rv)) {
     return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
   MOZ_ASSERT(!onSTSThread);
   if (onSTSThread) {
     return mozilla::pkix::Result::FATAL_ERROR_INVALID_STATE;
   }
 
-  RefPtr<OCSPRequest> request(new OCSPRequest(aiaLocation, originAttributes,
-                                              std::move(ocspRequest), timeout));
+  RefPtr<OCSPRequest> request(new OCSPRequest(
+      aiaLocation, originAttributes, ocspRequest, ocspRequestLength, timeout));
   rv = request->DispatchToMainThreadAndWait();
   if (NS_FAILED(rv)) {
     return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
   rv = request->GetResponse(result);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_MALFORMED_URI) {
       return mozilla::pkix::Result::ERROR_CERT_BAD_ACCESS_LOCATION;
--- a/security/manager/ssl/nsNSSCallbacks.h
+++ b/security/manager/ssl/nsNSSCallbacks.h
@@ -9,29 +9,30 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Vector.h"
 #include "nspr.h"
 #include "nsString.h"
 #include "pk11func.h"
+#include "mozpkix/pkix.h"
 #include "mozpkix/pkixtypes.h"
 
 using mozilla::OriginAttributes;
 using mozilla::TimeDuration;
 using mozilla::Vector;
 
 class nsILoadGroup;
 
 char* PK11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg);
 
 void HandshakeCallback(PRFileDesc* fd, void* client_data);
 SECStatus CanFalseStartCallback(PRFileDesc* fd, void* client_data,
                                 PRBool* canFalseStart);
 
-mozilla::pkix::Result DoOCSPRequest(const nsCString& aiaLocation,
-                                    const OriginAttributes& originAttributes,
-                                    Vector<uint8_t>&& ocspRequest,
-                                    TimeDuration timeout,
-                                    /*out*/ Vector<uint8_t>& result);
+mozilla::pkix::Result DoOCSPRequest(
+    const nsCString& aiaLocation, const OriginAttributes& originAttributes,
+    uint8_t (&ocspRequest)[mozilla::pkix::OCSP_REQUEST_MAX_LENGTH],
+    size_t ocspRequestLength, TimeDuration timeout,
+    /*out*/ Vector<uint8_t>& result);
 
 #endif  // nsNSSCallbacks_h
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -287,27 +287,16 @@ void AndroidBridge::GetExtensionFromMime
 
   auto jstrExt = GeckoAppShell::GetExtensionFromMimeType(aMimeType);
 
   if (jstrExt) {
     aFileExt = jstrExt->ToCString();
   }
 }
 
-bool AndroidBridge::GetClipboardText(nsAString& aText) {
-  ALOG_BRIDGE("AndroidBridge::GetClipboardText");
-
-  auto text = Clipboard::GetText(GeckoAppShell::GetApplicationContext());
-
-  if (text) {
-    aText = text->ToString();
-  }
-  return !!text;
-}
-
 int AndroidBridge::GetScreenDepth() {
   ALOG_BRIDGE("%s", __PRETTY_FUNCTION__);
 
   static int sDepth = 0;
   if (sDepth) return sDepth;
 
   const int DEFAULT_DEPTH = 16;
 
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -108,18 +108,16 @@ class AndroidBridge final {
   bool GetHWEncoderCapability();
   bool GetHWDecoderCapability();
 
   void GetMimeTypeFromExtensions(const nsACString& aFileExt,
                                  nsCString& aMimeType);
   void GetExtensionFromMimeType(const nsACString& aMimeType,
                                 nsACString& aFileExt);
 
-  bool GetClipboardText(nsAString& aText);
-
   int GetScreenDepth();
 
   void Vibrate(const nsTArray<uint32_t>& aPattern);
 
   void GetSystemColors(AndroidSystemColors* aColors);
 
   void GetIconForExtension(const nsACString& aFileExt, uint32_t aIconSize,
                            uint8_t* const aBuf);
--- a/widget/android/nsClipboard.cpp
+++ b/widget/android/nsClipboard.cpp
@@ -1,100 +1,152 @@
 /* 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/ContentChild.h"
 #include "nsClipboard.h"
+#include "FennecJNIWrappers.h"
 #include "nsISupportsPrimitives.h"
-#include "AndroidBridge.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
-#include "nsXULAppAPI.h"
+#include "nsPrimitiveHelpers.h"
 
 using namespace mozilla;
-using mozilla::dom::ContentChild;
 
 NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
 
 /* The Android clipboard only supports text and doesn't support mime types
  * so we assume all clipboard data is text/unicode for now. Documentation
  * indicates that support for other data types is planned for future
  * releases.
  */
 
 nsClipboard::nsClipboard() {}
 
 NS_IMETHODIMP
 nsClipboard::SetData(nsITransferable *aTransferable, nsIClipboardOwner *anOwner,
                      int32_t aWhichClipboard) {
   if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
 
-  nsCOMPtr<nsISupports> tmp;
-  nsresult rv =
-      aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(tmp));
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(tmp);
-  // No support for non-text data
-  NS_ENSURE_TRUE(supportsString, NS_ERROR_NOT_IMPLEMENTED);
-  nsAutoString buffer;
-  supportsString->GetData(buffer);
+  if (!jni::IsAvailable()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsTArray<nsCString> flavors;
+  aTransferable->FlavorsTransferableCanImport(flavors);
+
+  nsAutoString html;
+  nsAutoString text;
 
-  java::Clipboard::SetText(java::GeckoAppShell::GetApplicationContext(),
-                           buffer);
-  return NS_OK;
+  for (auto &flavorStr : flavors) {
+    if (flavorStr.EqualsLiteral(kUnicodeMime)) {
+      nsCOMPtr<nsISupports> item;
+      nsresult rv =
+          aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(item));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        continue;
+      }
+      nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(item);
+      if (supportsString) {
+        supportsString->GetData(text);
+      }
+    } else if (flavorStr.EqualsLiteral(kHTMLMime)) {
+      nsCOMPtr<nsISupports> item;
+      nsresult rv =
+          aTransferable->GetTransferData(kHTMLMime, getter_AddRefs(item));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        continue;
+      }
+      nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(item);
+      if (supportsString) {
+        supportsString->GetData(html);
+      }
+    }
+  }
+
+  if (!html.IsEmpty()) {
+    java::Clipboard::SetHTML(GeckoAppShell::GetApplicationContext(), text,
+                             html);
+    return NS_OK;
+  }
+  if (!text.IsEmpty()) {
+    java::Clipboard::SetText(GeckoAppShell::GetApplicationContext(), text);
+    return NS_OK;
+  }
+
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) {
   if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
 
-  nsAutoString buffer;
-  if (!AndroidBridge::Bridge()) return NS_ERROR_NOT_IMPLEMENTED;
-  if (!AndroidBridge::Bridge()->GetClipboardText(buffer))
-    return NS_ERROR_UNEXPECTED;
+  if (!jni::IsAvailable()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
-  nsresult rv;
-  nsCOMPtr<nsISupportsString> dataWrapper =
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsTArray<nsCString> flavors;
+  aTransferable->FlavorsTransferableCanImport(flavors);
 
-  rv = dataWrapper->SetData(buffer);
-  NS_ENSURE_SUCCESS(rv, rv);
+  for (auto &flavorStr : flavors) {
+    if (flavorStr.EqualsLiteral(kUnicodeMime) ||
+        flavorStr.EqualsLiteral(kHTMLMime)) {
+      auto text =
+          Clipboard::GetData(GeckoAppShell::GetApplicationContext(), flavorStr);
+      if (!text) {
+        continue;
+      }
+      nsString buffer = text->ToString();
+      if (buffer.IsEmpty()) {
+        continue;
+      }
+      nsCOMPtr<nsISupports> wrapper;
+      nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, buffer.get(),
+                                                 buffer.Length() * 2,
+                                                 getter_AddRefs(wrapper));
+      if (wrapper) {
+        aTransferable->SetTransferData(flavorStr.get(), wrapper);
+        return NS_OK;
+      }
+    }
+  }
 
-  // If our data flavor has already been added, this will fail. But we don't
-  // care
-  aTransferable->AddDataFlavor(kUnicodeMime);
-
-  nsCOMPtr<nsISupports> nsisupportsDataWrapper = do_QueryInterface(dataWrapper);
-  rv = aTransferable->SetTransferData(kUnicodeMime, nsisupportsDataWrapper);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsClipboard::EmptyClipboard(int32_t aWhichClipboard) {
   if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
+
+  if (!jni::IsAvailable()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   java::Clipboard::ClearText(java::GeckoAppShell::GetApplicationContext());
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsClipboard::HasDataMatchingFlavors(const char **aFlavorList, uint32_t aLength,
                                     int32_t aWhichClipboard, bool *aHasText) {
   *aHasText = false;
   if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED;
 
+  if (!jni::IsAvailable()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   for (uint32_t k = 0; k < aLength; k++) {
-    if (strcmp(aFlavorList[k], kUnicodeMime) == 0) {
-      *aHasText = java::Clipboard::HasText(
-          java::GeckoAppShell::GetApplicationContext());
-      break;
+    bool hasData =
+        java::Clipboard::HasData(java::GeckoAppShell::GetApplicationContext(),
+                                 NS_ConvertASCIItoUTF16(aFlavorList[k]));
+    if (hasData) {
+      *aHasText = true;
+      return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsClipboard::SupportsSelectionClipboard(bool *aIsSupported) {