зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
486b64e4bf
|
@ -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]
|
||||
|
|
|
@ -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);
|
|
@ -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:
|
||||
|
|
|
@ -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<void*>(table.getAnyRefLocForCompiledCode(index));
|
||||
return const_cast<void*>(table.getShortlivedAnyRefLocForCompiledCode(index));
|
||||
}
|
||||
|
||||
/* static */ uint32_t /* infallible */
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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<HeapPtr<JSObject*>&>(objects_[index]).unsafeUnbarrieredForTracing();
|
||||
}
|
||||
|
||||
void Table::setAnyFunc(uint32_t index, void* code, const Instance* instance) {
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace wasm {
|
|||
// 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<
|
||||
|
@ -87,7 +87,7 @@ class Table : public ShareableBase<Table> {
|
|||
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<void*>(aDocShell));
|
||||
nsCOMPtr<nsIPresShell> shell(pres_shell(aDocShell));
|
||||
if (shell) {
|
||||
|
|
|
@ -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<T*>(sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
CRAndStorage<kInlineCapacity, 0> mTail;
|
||||
|
|
|
@ -506,6 +506,67 @@ static_assert(sizeof(Vector<Incomplete, 0>) ==
|
|||
|
||||
#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();
|
||||
|
@ -516,4 +577,5 @@ int main() {
|
|||
VectorTesting::testReplaceRawBuffer();
|
||||
VectorTesting::testInsert();
|
||||
VectorTesting::testPodResizeToFit();
|
||||
TestVectorBeginNonNull();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<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
|
||||
|
@ -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,
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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<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
|
||||
|
@ -104,7 +106,7 @@ class OCSPRequest final : public nsIStreamLoaderObserver, public nsIRunnable {
|
|||
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;
|
||||
|
@ -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<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());
|
||||
|
@ -279,10 +284,7 @@ OCSPRequest::Run() {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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<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()));
|
||||
|
@ -455,8 +460,8 @@ mozilla::pkix::Result DoOCSPRequest(const nsCString& aiaLocation,
|
|||
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;
|
||||
|
|
|
@ -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<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
|
||||
|
|
|
@ -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__);
|
||||
|
||||
|
|
|
@ -113,8 +113,6 @@ class AndroidBridge final {
|
|||
void GetExtensionFromMimeType(const nsACString& aMimeType,
|
||||
nsACString& aFileExt);
|
||||
|
||||
bool GetClipboardText(nsAString& aText);
|
||||
|
||||
int GetScreenDepth();
|
||||
|
||||
void Vibrate(const nsTArray<uint32_t>& aPattern);
|
||||
|
|
|
@ -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<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;
|
||||
}
|
||||
|
||||
java::Clipboard::SetText(java::GeckoAppShell::GetApplicationContext(),
|
||||
buffer);
|
||||
return NS_OK;
|
||||
nsTArray<nsCString> flavors;
|
||||
aTransferable->FlavorsTransferableCanImport(flavors);
|
||||
|
||||
nsAutoString html;
|
||||
nsAutoString text;
|
||||
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче