Backed out changeset 22970015ff0c (bug 1815739) for saveAContentPdfDocument related junit failures. CLOSED TREE

This commit is contained in:
Stanca Serban 2023-11-13 19:51:04 +02:00
Родитель e719890e40
Коммит c3f330e9fb
14 изменённых файлов: 2 добавлений и 648 удалений

Просмотреть файл

@ -1,47 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GeckoViewContentChannel.h"
#include "GeckoViewInputStream.h"
#include "mozilla/java/ContentInputStreamWrappers.h"
using namespace mozilla;
GeckoViewContentChannel::GeckoViewContentChannel(nsIURI* aURI) {
SetURI(aURI);
SetOriginalURI(aURI);
}
NS_IMETHODIMP
GeckoViewContentChannel::OpenContentStream(bool aAsync,
nsIInputStream** aResult,
nsIChannel** aChannel) {
nsCOMPtr<nsIURI> uri;
nsresult rv = GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
nsAutoCString spec;
rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
bool isReadable = GeckoViewContentInputStream::isReadable(spec);
if (!isReadable) {
return NS_ERROR_FILE_NOT_FOUND;
}
nsCOMPtr<nsIInputStream> inputStream;
rv = GeckoViewContentInputStream::getInstance(spec,
getter_AddRefs(inputStream));
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(!inputStream)) {
return NS_ERROR_MALFORMED_URI;
}
inputStream.forget(aResult);
return NS_OK;
}

Просмотреть файл

@ -1,23 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GeckoViewContentChannel_h__
#define GeckoViewContentChannel_h__
#include "nsBaseChannel.h"
class GeckoViewContentChannel final : public nsBaseChannel {
public:
explicit GeckoViewContentChannel(nsIURI* aUri);
private:
~GeckoViewContentChannel() = default;
nsresult OpenContentStream(bool async, nsIInputStream** result,
nsIChannel** channel) override;
};
#endif // !GeckoViewContentChannel_h__

Просмотреть файл

@ -1,54 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:ts=4 sw=2 sts=2 et cin:
/* 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 "GeckoViewContentProtocolHandler.h"
#include "GeckoViewContentChannel.h"
#include "nsStandardURL.h"
#include "nsURLHelper.h"
#include "nsIURIMutator.h"
#include "nsNetUtil.h"
#include "mozilla/ResultExtensions.h"
//-----------------------------------------------------------------------------
nsresult GeckoViewContentProtocolHandler::Init() { return NS_OK; }
NS_IMPL_ISUPPORTS(GeckoViewContentProtocolHandler, nsIProtocolHandler,
nsISupportsWeakReference)
//-----------------------------------------------------------------------------
// nsIProtocolHandler methods:
NS_IMETHODIMP
GeckoViewContentProtocolHandler::GetScheme(nsACString& result) {
result.AssignLiteral("content");
return NS_OK;
}
NS_IMETHODIMP
GeckoViewContentProtocolHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo,
nsIChannel** result) {
nsresult rv;
RefPtr<GeckoViewContentChannel> chan = new GeckoViewContentChannel(uri);
rv = chan->SetLoadInfo(aLoadInfo);
if (NS_FAILED(rv)) {
return rv;
}
chan.forget(result);
return NS_OK;
}
NS_IMETHODIMP
GeckoViewContentProtocolHandler::AllowPort(int32_t port, const char* scheme,
bool* result) {
// don't override anything.
*result = false;
return NS_OK;
}

Просмотреть файл

@ -1,27 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GeckoViewContentProtocolHandler_h__
#define GeckoViewContentProtocolHandler_h__
#include "nsIProtocolHandler.h"
#include "nsWeakReference.h"
class nsIURIMutator;
class GeckoViewContentProtocolHandler : public nsIProtocolHandler,
public nsSupportsWeakReference {
virtual ~GeckoViewContentProtocolHandler() = default;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
GeckoViewContentProtocolHandler() = default;
[[nodiscard]] nsresult Init();
};
#endif // !GeckoViewContentProtocolHandler_h__

Просмотреть файл

@ -1,110 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cin: */
/* 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 "GeckoViewInputStream.h"
#include "nsStreamUtils.h"
NS_IMPL_ISUPPORTS(GeckoViewInputStream, nsIInputStream);
NS_IMETHODIMP
GeckoViewInputStream::Close() {
mClosed = true;
mInstance->Close();
return NS_OK;
}
NS_IMETHODIMP
GeckoViewInputStream::Available(uint64_t* aCount) {
if (mClosed) {
return NS_BASE_STREAM_CLOSED;
}
*aCount = static_cast<uint64_t>(mInstance->Available());
return NS_OK;
}
NS_IMETHODIMP
GeckoViewInputStream::StreamStatus() {
return mClosed ? NS_BASE_STREAM_CLOSED : NS_OK;
}
NS_IMETHODIMP
GeckoViewInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aReadCount) {
return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
}
NS_IMETHODIMP
GeckoViewInputStream::ReadSegments(nsWriteSegmentFun writer, void* aClosure,
uint32_t aCount, uint32_t* result) {
NS_ASSERTION(result, "null ptr");
if (mClosed) {
return NS_BASE_STREAM_CLOSED;
}
auto bufferAddress =
static_cast<const char*>(mInstance->MBuffer()->Address());
uint32_t segmentPos = static_cast<uint32_t>(mInstance->MPos());
int32_t dataLength = 0;
nsresult rv;
*result = 0;
while (aCount) {
rv = mInstance->Read(static_cast<int64_t>(aCount), &dataLength);
if (NS_FAILED(rv)) {
return NS_BASE_STREAM_OSERROR;
}
if (dataLength == -1) {
break;
}
uint32_t uDataLength = static_cast<uint32_t>(dataLength);
uint32_t written;
rv = writer(this, aClosure, bufferAddress + segmentPos, *result,
uDataLength, &written);
if (NS_FAILED(rv)) {
// InputStreams do not propagate errors to caller.
break;
}
NS_ASSERTION(written > 0, "Must have written something");
*result += written;
aCount -= written;
segmentPos = static_cast<uint32_t>(mInstance->ConsumedData(written));
}
return NS_OK;
}
NS_IMETHODIMP
GeckoViewInputStream::IsNonBlocking(bool* aNonBlocking) {
*aNonBlocking = true;
return NS_OK;
}
bool GeckoViewInputStream::isClosed() const { return mInstance->IsClosed(); }
bool GeckoViewContentInputStream::isReadable(const nsAutoCString& aUri) {
return mozilla::java::ContentInputStream::IsReadable(
mozilla::jni::StringParam(aUri));
}
nsresult GeckoViewContentInputStream::getInstance(const nsAutoCString& aUri,
nsIInputStream** aInstance) {
RefPtr<GeckoViewContentInputStream> instance =
new GeckoViewContentInputStream(aUri);
if (instance->isClosed()) {
return NS_ERROR_FILE_NOT_FOUND;
}
*aInstance = instance.forget().take();
return NS_OK;
}

Просмотреть файл

@ -1,43 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GeckoViewInputStream_h__
#define GeckoViewInputStream_h__
#include "mozilla/java/GeckoViewInputStreamWrappers.h"
#include "mozilla/java/ContentInputStreamWrappers.h"
#include "nsIInputStream.h"
class GeckoViewInputStream : public nsIInputStream {
NS_DECL_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
explicit GeckoViewInputStream(
mozilla::java::GeckoViewInputStream::LocalRef aInstance)
: mInstance(aInstance){};
bool isClosed() const;
protected:
virtual ~GeckoViewInputStream() = default;
private:
mozilla::java::GeckoViewInputStream::LocalRef mInstance;
bool mClosed{false};
};
class GeckoViewContentInputStream final : public GeckoViewInputStream {
public:
static nsresult getInstance(const nsAutoCString& aUri,
nsIInputStream** aInstance);
static bool isReadable(const nsAutoCString& aUri);
private:
explicit GeckoViewContentInputStream(const nsAutoCString& aUri)
: GeckoViewInputStream(mozilla::java::ContentInputStream::GetInstance(
mozilla::jni::StringParam(aUri))) {}
};
#endif // !GeckoViewInputStream_h__

Просмотреть файл

@ -77,20 +77,6 @@ Classes = [
'headers': ['GeckoViewExternalAppService.h'],
'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS,
},
{
'cid': '{a8f4582e-4b47-4e06-970d-b94b76977bf7}',
'contract_ids': ['@mozilla.org/network/protocol;1?name=content'],
'type': 'GeckoViewContentProtocolHandler',
'headers': ['./GeckoViewContentProtocolHandler.h'],
'protocol_config': {
'scheme': 'content',
'flags': [
'URI_IS_POTENTIALLY_TRUSTWORTHY',
'URI_IS_LOCAL_RESOURCE',
'URI_DANGEROUS_TO_LOAD',
],
},
},
]
if defined('MOZ_ANDROID_HISTORY'):

Просмотреть файл

@ -5,19 +5,13 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SOURCES += [
"GeckoViewContentChannel.cpp",
"GeckoViewContentProtocolHandler.cpp",
"GeckoViewExternalAppService.cpp",
"GeckoViewInputStream.cpp",
"GeckoViewOutputStream.cpp",
"GeckoViewStreamListener.cpp",
]
EXPORTS += [
"GeckoViewContentChannel.h",
"GeckoViewContentProtocolHandler.h",
"GeckoViewExternalAppService.h",
"GeckoViewInputStream.h",
"GeckoViewOutputStream.h",
"GeckoViewStreamListener.h",
]

Просмотреть файл

@ -158,20 +158,4 @@ class PdfCreationTest : BaseSessionTest() {
}
}
}
@NullDelegate(Autofill.Delegate::class)
@Test
fun saveAContentPdfDocument() {
activityRule.scenario.onActivity {
val originalBytes = getTestBytes(HELLO_PDF_WORLD_PDF_PATH)
TestContentProvider.setTestData(originalBytes, "application/pdf")
mainSession.loadUri("content://org.mozilla.geckoview.test.provider/pdf")
mainSession.waitForPageStop()
val response = mainSession.pdfFileSaver.save()
sessionRule.waitForResult(response).let {
assertThat("The PDF File must the same as the original one.", it.body?.readBytes(), equalTo(originalBytes))
}
}
}
}

Просмотреть файл

@ -1,140 +0,0 @@
/* 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/. */
package org.mozilla.geckoview;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Process;
import android.util.Log;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import java.io.IOException;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.annotation.WrapForJNI;
/**
* This class provides an {@link OutputStream} wrapper for a Gecko nsIOutputStream (or really,
* nsIRequest).
*/
/* package */ class ContentInputStream extends GeckoViewInputStream {
private static final String LOGTAG = "ContentInputStream";
private static final byte[][] HEADERS = {{'%', 'P', 'D', 'F', '-'}};
private AssetFileDescriptor mFd;
ContentInputStream(final @NonNull String aUri) {
final Uri uri = Uri.parse(aUri);
final Context context = GeckoAppShell.getApplicationContext();
final ContentResolver cr = context.getContentResolver();
try {
mFd = cr.openAssetFileDescriptor(uri, "r");
setInputStream(mFd.createInputStream());
if (!checkHeaders(HEADERS)) {
Log.e(LOGTAG, "Cannot open the uri: " + aUri + " (invalid header)");
close();
}
} catch (final IOException | SecurityException e) {
Log.e(LOGTAG, "Cannot open the uri: " + aUri, e);
close();
}
}
@Override
public void close() {
if (mFd != null) {
try {
mFd.close();
} catch (final IOException e) {
Log.e(LOGTAG, "Cannot close the file descriptor", e);
} finally {
mFd = null;
}
}
super.close();
}
private static boolean wasGrantedPermission(
final @NonNull Context aCtx, final @NonNull Uri aUri) {
// For reference:
// https://developer.android.com/topic/security/risks/content-resolver#mitigations_2
final int pid = Process.myPid();
final int uid = Process.myUid();
return aCtx.checkUriPermission(aUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
private static boolean isExported(final @NonNull Context aCtx, final @NonNull Uri aUri) {
// For reference:
// https://developer.android.com/topic/security/risks/content-resolver#mitigations_2
final String authority = aUri.getAuthority();
final PackageManager packageManager = aCtx.getPackageManager();
if (authority == null || packageManager == null) {
return false;
}
final ProviderInfo info = packageManager.resolveContentProvider(authority, 0);
if (info == null) {
return false;
}
// We check that the provider is exported:
// https://developer.android.com/reference/android/content/pm/ComponentInfo?hl=en#exported
// or is GV itself (when testing GV, the provider is GV itself).
final String packageName = aCtx.getPackageName();
return info.exported || (packageName != null && packageName.equals(info.packageName));
}
private static boolean belongsToCurrentApplication(
final @NonNull Context aCtx, final @NonNull Uri aUri) {
// For reference:
// https://developer.android.com/topic/security/risks/content-resolver#mitigations_2
final String authority = aUri.getAuthority();
final PackageManager packageManager = aCtx.getPackageManager();
if (authority == null || packageManager == null) {
return false;
}
final ProviderInfo info = packageManager.resolveContentProvider(authority, 0);
if (info == null) {
return false;
}
// We check that the provider is GV itself (when testing GV, the provider is GV itself).
final String packageName = aCtx.getPackageName();
return packageName != null && packageName.equals(info.packageName);
}
@WrapForJNI
@AnyThread
private static boolean isReadable(final @NonNull String aUri) {
final Uri uri = Uri.parse(aUri);
final Context context = GeckoAppShell.getApplicationContext();
if ((isExported(context, uri) && wasGrantedPermission(context, uri))
|| belongsToCurrentApplication(context, uri)) {
try {
final ContentResolver cr = context.getContentResolver();
cr.openAssetFileDescriptor(uri, "r").close();
return true;
} catch (final IOException | SecurityException e) {
// A SecurityException could happen if the uri is no more valid.
Log.e(LOGTAG, "Cannot read the uri: " + uri, e);
}
}
return false;
}
@WrapForJNI
@AnyThread
private static @NonNull GeckoViewInputStream getInstance(final @NonNull String aUri) {
return new ContentInputStream(aUri);
}
}

Просмотреть файл

@ -1,163 +0,0 @@
/* 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/. */
package org.mozilla.geckoview;
import android.util.Log;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import org.mozilla.gecko.annotation.WrapForJNI;
/** This class provides a Gecko nsIInputStream wrapper for a Java {@link InputStream}. */
@WrapForJNI
@AnyThread
/* package */ class GeckoViewInputStream {
private static final String LOGTAG = "GeckoViewInputStream";
private static final int BUFFER_SIZE = 4096;
protected final ByteBuffer mBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
private ReadableByteChannel mChannel;
private InputStream mIs = null;
private boolean mMustGetData = true;
private int mPos = 0;
private int mSize;
/**
* Set an input stream.
*
* @param is the {@link InputStream} to set.
*/
protected void setInputStream(final @NonNull InputStream is) {
mIs = is;
mChannel = Channels.newChannel(is);
}
/**
* Check if there is a stream.
*
* @return true if there is no stream.
*/
public boolean isClosed() {
return mIs == null;
}
/**
* Called by native code to get the number of available bytes in the underlying stream.
*
* @return the number of available bytes.
*/
public int available() {
if (mIs == null || mSize == -1) {
return 0;
}
try {
return Math.max(mIs.available(), mMustGetData ? 0 : mSize - mPos);
} catch (final IOException e) {
Log.e(LOGTAG, "Cannot get the number of available bytes", e);
return 0;
}
}
/** Close the underlying stream. */
public void close() {
if (mIs == null) {
return;
}
try {
mChannel.close();
} catch (final IOException e) {
Log.e(LOGTAG, "Cannot close the channel", e);
} finally {
mChannel = null;
}
try {
mIs.close();
} catch (final IOException e) {
Log.e(LOGTAG, "Cannot close the stream", e);
} finally {
mIs = null;
}
}
/**
* Called by native code to notify that the data have been consumed.
*
* @param length the number of consumed bytes.
* @return the position in the buffer.
*/
public long consumedData(final int length) {
mPos += length;
if (mPos >= mSize) {
mPos = 0;
mMustGetData = true;
}
return mPos;
}
/**
* Check that the underlying stream starts with one of the given headers.
*
* @param headers the headers to check.
* @return true if one of the headers is found.
*/
protected boolean checkHeaders(final @NonNull byte[][] headers) throws IOException {
read(0);
for (final byte[] header : headers) {
final int headerLength = header.length;
if (mSize < headerLength) {
continue;
}
int i = 0;
for (; i < headerLength; i++) {
if (mBuffer.get(i) != header[i]) {
break;
}
}
if (i == headerLength) {
return true;
}
}
return false;
}
/**
* Called by native code to read some bytes in the underlying stream.
*
* @param aCount the number of bytes to read.
* @return the number of read bytes, -1 in case of EOF.
* @throws IOException if an error occurs.
*/
@WrapForJNI(exceptionMode = "nsresult")
public int read(final long aCount) throws IOException {
if (mIs == null) {
Log.e(LOGTAG, "The stream is closed.");
throw new IllegalStateException("Stream is closed");
}
if (!mMustGetData) {
return (int) Math.min((long) (mSize - mPos), aCount);
}
mMustGetData = false;
mBuffer.clear();
try {
mSize = mChannel.read(mBuffer);
} catch (final IOException e) {
Log.e(LOGTAG, "Cannot read some bytes", e);
throw e;
}
if (mSize == -1) {
return -1;
}
return (int) Math.min((long) mSize, aCount);
}
}

Просмотреть файл

@ -79,8 +79,7 @@ public final class SessionPdfFileSaver {
public GeckoResult<WebResponse> onValue(final WebResponse response) {
final int statusCode = response.statusCode != 0 ? response.statusCode : 200;
return GeckoResult.fromValue(
new WebResponse.Builder(
originalUrl.startsWith("content://") ? url : originalUrl)
new WebResponse.Builder(originalUrl)
.statusCode(statusCode)
.body(response.body)
.skipConfirmation(skipConfirmation)

Просмотреть файл

@ -205,7 +205,7 @@ export class GeckoViewNavigation extends GeckoViewModule {
// a privileged principal.
const isExternal =
navFlags & Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
if (!isExternal || Services.io.newURI(uri).schemeIs("content")) {
if (!isExternal) {
// Always use the system principal as the triggering principal
// for user-initiated (ie. no referrer session and not external)
// loads. See discussion in bug 1573860.

Просмотреть файл

@ -33,7 +33,6 @@ classes_with_WrapForJNI = [
"Clipboard",
"CodecProxy",
"CompositorSurfaceManager",
"ContentInputStream",
"EnterpriseRoots",
"EventCallback",
"EventDispatcher",
@ -59,7 +58,6 @@ classes_with_WrapForJNI = [
"GeckoSurfaceTexture",
"GeckoSystemStateListener",
"GeckoThread",
"GeckoViewInputStream",
"GeckoVRManager",
"GeckoVideoInfo",
"GeckoWebExecutor",