diff --git a/editor/libeditor/tests/mochitest.ini b/editor/libeditor/tests/mochitest.ini index 77c6d722ad5c..2acef3bebc39 100644 --- a/editor/libeditor/tests/mochitest.ini +++ b/editor/libeditor/tests/mochitest.ini @@ -43,7 +43,6 @@ skip-if = toolkit == 'android' 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 @@ -61,11 +60,9 @@ skip-if = toolkit == 'android' [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' @@ -77,10 +74,8 @@ skip-if = toolkit == 'android' # bug 1299578 [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' @@ -225,7 +220,6 @@ skip-if = os == 'android' [test_bug1230473.html] [test_bug1247483.html] subsuite = clipboard -skip-if = toolkit == 'android' [test_bug1248128.html] [test_bug1250010.html] [test_bug1257363.html] @@ -235,7 +229,6 @@ skip-if = toolkit == 'android' [test_bug1270235.html] [test_bug1306532.html] subsuite = clipboard -skip-if = toolkit == 'android' [test_bug1310912.html] skip-if = toolkit == 'android' # bug 1315898 [test_bug1314790.html] diff --git a/js/src/jit-test/tests/wasm/regress/table-of-anyref.js b/js/src/jit-test/tests/wasm/regress/table-of-anyref.js new file mode 100644 index 000000000000..50d2a31c246d --- /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); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 26f68ed4cb07..1660653ebdaf 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -11932,11 +11932,14 @@ class MWasmDerivedPointer : public MUnaryInstruction, 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: diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp index ba7749413bae..75c86c653494 100644 --- a/js/src/wasm/WasmInstance.cpp +++ b/js/src/wasm/WasmInstance.cpp @@ -857,6 +857,10 @@ Instance::tableInit(Instance* instance, uint32_t dstOffset, uint32_t srcOffset, // 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 */ @@ -868,7 +872,7 @@ Instance::tableGet(Instance* instance, uint32_t index, uint32_t tableIndex) { JSMSG_WASM_TABLE_OUT_OF_BOUNDS); return nullptr; } - return const_cast(table.getAnyRefLocForCompiledCode(index)); + return const_cast(table.getShortlivedAnyRefLocForCompiledCode(index)); } /* static */ uint32_t /* infallible */ diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index ba99ccc4fb11..86ebc189e212 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -729,8 +729,11 @@ class FunctionCompiler { } 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; } @@ -3160,8 +3163,8 @@ static bool EmitTableGet(FunctionCompiler& f) { 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)) { diff --git a/js/src/wasm/WasmTable.cpp b/js/src/wasm/WasmTable.cpp index 36bb60866346..6d5acd1d8aab 100644 --- a/js/src/wasm/WasmTable.cpp +++ b/js/src/wasm/WasmTable.cpp @@ -148,9 +148,9 @@ AnyRef Table::getAnyRef(uint32_t index) const { 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&>(objects_[index]).unsafeUnbarrieredForTracing(); } void Table::setAnyFunc(uint32_t index, void* code, const Instance* instance) { diff --git a/js/src/wasm/WasmTable.h b/js/src/wasm/WasmTable.h index 3c533a20d25f..78972fb1b3dd 100644 --- a/js/src/wasm/WasmTable.h +++ b/js/src/wasm/WasmTable.h @@ -38,7 +38,7 @@ namespace wasm { // the most appropriate representation for Cell::anyref. STATIC_ASSERT_ANYREF_IS_JSOBJECT; -typedef GCVector, 0, SystemAllocPolicy> TableAnyRefVector; +typedef GCVector, 0, SystemAllocPolicy> TableAnyRefVector; class Table : public ShareableBase { using InstanceSet = JS::WeakCache { 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); diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 3a8ca8639337..4e6ab977fd86 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -666,9 +666,10 @@ static nsIFrame* GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame) { 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 @@ -705,6 +706,12 @@ static bool RecomputePosition(nsIFrame* aFrame) { } } + // 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 diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index d0daea0b0739..c9afa0ed2163 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -453,7 +453,6 @@ static void SetAnimatable(nsCSSPropertyID aProperty, MOZ_ASSERT(aFrame); if (aAnimationValue.IsNull()) { - printf_stderr("[Boris] set null animatable\n"); aAnimatable = null_t(); return; } diff --git a/layout/tools/layout-debug/src/nsLayoutDebuggingTools.cpp b/layout/tools/layout-debug/src/nsLayoutDebuggingTools.cpp index ba879c20e78b..97cfa1f6b9f1 100644 --- a/layout/tools/layout-debug/src/nsLayoutDebuggingTools.cpp +++ b/layout/tools/layout-debug/src/nsLayoutDebuggingTools.cpp @@ -332,7 +332,7 @@ nsLayoutDebuggingTools::DumpContent() { } static void DumpFramesRecur(nsIDocShell* aDocShell, FILE* out) { -#ifdef DEBUG +#ifdef DEBUG_FRAME_DUMP fprintf(out, "webshell=%p \n", static_cast(aDocShell)); nsCOMPtr shell(pres_shell(aDocShell)); if (shell) { diff --git a/mfbt/Vector.h b/mfbt/Vector.h index 7f483161f919..696da9ab7ad8 100644 --- a/mfbt/Vector.h +++ b/mfbt/Vector.h @@ -403,7 +403,14 @@ class MOZ_NON_PARAM Vector final : private AllocPolicy { : 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(sizeof(T)); + } }; CRAndStorage mTail; diff --git a/mfbt/tests/TestVector.cpp b/mfbt/tests/TestVector.cpp index 158addef9520..1164e10266a8 100644 --- a/mfbt/tests/TestVector.cpp +++ b/mfbt/tests/TestVector.cpp @@ -506,6 +506,67 @@ static_assert(sizeof(Vector) == #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 bvec0; + MOZ_RELEASE_ASSERT(bvec0.length() == 0); + MOZ_RELEASE_ASSERT(bvec0.begin() != nullptr); + + Vector bvec1; + MOZ_RELEASE_ASSERT(bvec1.length() == 0); + MOZ_RELEASE_ASSERT(bvec1.begin() != nullptr); + + Vector bvec64; + MOZ_RELEASE_ASSERT(bvec64.length() == 0); + MOZ_RELEASE_ASSERT(bvec64.begin() != nullptr); + + Vector ivec0; + MOZ_RELEASE_ASSERT(ivec0.length() == 0); + MOZ_RELEASE_ASSERT(ivec0.begin() != nullptr); + + Vector ivec1; + MOZ_RELEASE_ASSERT(ivec1.length() == 0); + MOZ_RELEASE_ASSERT(ivec1.begin() != nullptr); + + Vector ivec64; + MOZ_RELEASE_ASSERT(ivec64.length() == 0); + MOZ_RELEASE_ASSERT(ivec64.begin() != nullptr); + + Vector lvec0; + MOZ_RELEASE_ASSERT(lvec0.length() == 0); + MOZ_RELEASE_ASSERT(lvec0.begin() != nullptr); + + Vector lvec1; + MOZ_RELEASE_ASSERT(lvec1.length() == 0); + MOZ_RELEASE_ASSERT(lvec1.begin() != nullptr); + + Vector lvec64; + MOZ_RELEASE_ASSERT(lvec64.length() == 0); + MOZ_RELEASE_ASSERT(lvec64.begin() != nullptr); + + // Vector 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 vbvec0; + MOZ_RELEASE_ASSERT(vbvec0.length() == 0); + MOZ_RELEASE_ASSERT(vbvec0.begin() != nullptr); + + Vector vbvec1; + MOZ_RELEASE_ASSERT(vbvec1.length() == 0); + MOZ_RELEASE_ASSERT(vbvec1.begin() != nullptr); + + Vector vbvec64; + MOZ_RELEASE_ASSERT(vbvec64.length() == 0); + MOZ_RELEASE_ASSERT(vbvec64.begin() != nullptr); +} + int main() { VectorTesting::testReserved(); VectorTesting::testConstRange(); @@ -516,4 +577,5 @@ int main() { VectorTesting::testReplaceRawBuffer(); VectorTesting::testInsert(); VectorTesting::testPodResizeToFit(); + TestVectorBeginNonNull(); } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/Clipboard.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/Clipboard.java index 78a50febb4a2..b7dda23eb35e 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/Clipboard.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/Clipboard.java @@ -8,37 +8,97 @@ 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() { } - @WrapForJNI(calledFrom = "gecko") + /** + * 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 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. @@ -50,8 +110,11 @@ public final class 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; } /** diff --git a/mobile/android/modules/ActionBarHandler.jsm b/mobile/android/modules/ActionBarHandler.jsm index bcf24be5eeda..26f6321c8d08 100644 --- a/mobile/android/modules/ActionBarHandler.jsm +++ b/mobile/android/modules/ActionBarHandler.jsm @@ -1,4 +1,5 @@ // -*- 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/. */ @@ -442,18 +443,11 @@ var ActionBarHandler = { }, 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"); }, @@ -480,10 +474,7 @@ var ActionBarHandler = { }, 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); diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index 2616378b3aa3..2a6ebbcfad44 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -542,76 +542,57 @@ Result NSSCertDBTrustDomain::CheckRevocation( 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 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 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 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 @@ -622,9 +603,9 @@ Result NSSCertDBTrustDomain::CheckRevocation( 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; } @@ -632,6 +613,7 @@ Result NSSCertDBTrustDomain::CheckRevocation( rv == Result::ERROR_REVOKED_CERTIFICATE) { return rv; } + if (stapledOCSPResponseResult != Success) { MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid " @@ -645,6 +627,36 @@ Result NSSCertDBTrustDomain::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, diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index 6370cf61fabc..85cf66bce7a9 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -202,6 +202,15 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { 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! diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index 5ee7bb97bd55..c0178db45bd7 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -14,6 +14,7 @@ #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" @@ -65,9 +66,10 @@ const uint32_t KEA_NOT_SUPPORTED = 1; class OCSPRequest final : public nsIStreamLoaderObserver, public nsIRunnable { public: - OCSPRequest(const nsCString& aiaLocation, + OCSPRequest(const nsACString& aiaLocation, const OriginAttributes& originAttributes, - Vector&& ocspRequest, TimeDuration timeout); + const uint8_t (&ocspRequest)[OCSP_REQUEST_MAX_LENGTH], + size_t ocspRequestLength, TimeDuration timeout); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSISTREAMLOADEROBSERVER @@ -104,7 +106,7 @@ class OCSPRequest final : public nsIStreamLoaderObserver, public nsIRunnable { nsCOMPtr mLoader; const nsCString mAIALocation; const OriginAttributes mOriginAttributes; - const Vector mPOSTData; + const mozilla::Span mPOSTData; const TimeDuration mTimeout; nsCOMPtr mTimeoutTimer; TimeStamp mStartTime; @@ -114,20 +116,23 @@ class OCSPRequest final : public nsIStreamLoaderObserver, public nsIRunnable { NS_IMPL_ISUPPORTS(OCSPRequest, nsIStreamLoaderObserver, nsIRunnable) -OCSPRequest::OCSPRequest(const nsCString& aiaLocation, +OCSPRequest::OCSPRequest(const nsACString& aiaLocation, const OriginAttributes& originAttributes, - Vector&& 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(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()); @@ -279,10 +284,7 @@ OCSPRequest::Run() { } nsCOMPtr uploadStream; - rv = NS_NewByteInputStream( - getter_AddRefs(uploadStream), - MakeSpan(reinterpret_cast(mPOSTData.begin()), - mPOSTData.length())); + rv = NS_NewByteInputStream(getter_AddRefs(uploadStream), mPOSTData); if (NS_FAILED(rv)) { return NotifyDone(rv, lock); } @@ -425,16 +427,19 @@ void OCSPRequest::OnTimeout(nsITimer* timer, void* closure) { self->NotifyDone(NS_ERROR_NET_TIMEOUT, lock); } -mozilla::pkix::Result DoOCSPRequest(const nsCString& aiaLocation, - const OriginAttributes& originAttributes, - Vector&& ocspRequest, - TimeDuration timeout, - /*out*/ Vector& 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& 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())); @@ -455,8 +460,8 @@ mozilla::pkix::Result DoOCSPRequest(const nsCString& aiaLocation, return mozilla::pkix::Result::FATAL_ERROR_INVALID_STATE; } - RefPtr request(new OCSPRequest(aiaLocation, originAttributes, - std::move(ocspRequest), timeout)); + RefPtr request(new OCSPRequest( + aiaLocation, originAttributes, ocspRequest, ocspRequestLength, timeout)); rv = request->DispatchToMainThreadAndWait(); if (NS_FAILED(rv)) { return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; diff --git a/security/manager/ssl/nsNSSCallbacks.h b/security/manager/ssl/nsNSSCallbacks.h index aa58958d9c0d..b9fa2ee3666b 100644 --- a/security/manager/ssl/nsNSSCallbacks.h +++ b/security/manager/ssl/nsNSSCallbacks.h @@ -14,6 +14,7 @@ #include "nspr.h" #include "nsString.h" #include "pk11func.h" +#include "mozpkix/pkix.h" #include "mozpkix/pkixtypes.h" using mozilla::OriginAttributes; @@ -28,10 +29,10 @@ 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&& ocspRequest, - TimeDuration timeout, - /*out*/ Vector& 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& result); #endif // nsNSSCallbacks_h diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 9256698e1367..4ea7c8ec6d6e 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -292,17 +292,6 @@ void AndroidBridge::GetExtensionFromMimeType(const nsACString& aMimeType, } } -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__); diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 2717ac956661..91bbb9438e50 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -113,8 +113,6 @@ class AndroidBridge final { void GetExtensionFromMimeType(const nsACString& aMimeType, nsACString& aFileExt); - bool GetClipboardText(nsAString& aText); - int GetScreenDepth(); void Vibrate(const nsTArray& aPattern); diff --git a/widget/android/nsClipboard.cpp b/widget/android/nsClipboard.cpp index 436308b6ac36..80ab0ff23abc 100644 --- a/widget/android/nsClipboard.cpp +++ b/widget/android/nsClipboard.cpp @@ -2,16 +2,14 @@ * 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) @@ -28,52 +26,100 @@ nsClipboard::SetData(nsITransferable *aTransferable, nsIClipboardOwner *anOwner, int32_t aWhichClipboard) { if (aWhichClipboard != kGlobalClipboard) return NS_ERROR_NOT_IMPLEMENTED; - nsCOMPtr tmp; - nsresult rv = - aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(tmp)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr 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; + } - java::Clipboard::SetText(java::GeckoAppShell::GetApplicationContext(), - buffer); - return NS_OK; + nsTArray flavors; + aTransferable->FlavorsTransferableCanImport(flavors); + + nsAutoString html; + nsAutoString text; + + for (auto &flavorStr : flavors) { + if (flavorStr.EqualsLiteral(kUnicodeMime)) { + nsCOMPtr item; + nsresult rv = + aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(item)); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + nsCOMPtr supportsString = do_QueryInterface(item); + if (supportsString) { + supportsString->GetData(text); + } + } else if (flavorStr.EqualsLiteral(kHTMLMime)) { + nsCOMPtr item; + nsresult rv = + aTransferable->GetTransferData(kHTMLMime, getter_AddRefs(item)); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + nsCOMPtr 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 dataWrapper = - do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + nsTArray 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 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 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; @@ -85,11 +131,17 @@ nsClipboard::HasDataMatchingFlavors(const char **aFlavorList, uint32_t aLength, *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; } }