Bug 1620657 - Add a native impl of nsIExternalHelperAppService for Android r=mattwoodrow,geckoview-reviewers,droeh

This is needed in order to implement `CreateListener()`, which is used
when DocumentChannel is enabled.

Differential Revision: https://phabricator.services.mozilla.com/D67682

--HG--
extra : moz-landing-system : lando
This commit is contained in:
James Willcox 2020-04-10 15:53:19 +00:00
parent da72652618
commit 9b0c905638
8 changed files with 204 additions and 84 deletions

View File

@ -64,19 +64,6 @@ Classes = [
'jsm': 'resource://gre/modules/URIFixup.jsm',
'constructor': 'URIFixupInfo',
},
{
'cid': '{a7f800e0-4306-11d4-98d0-001083010e9b}',
'contract_ids': [
'@mozilla.org/mime;1',
'@mozilla.org/uriloader/external-helper-app-service;1',
'@mozilla.org/uriloader/external-protocol-service;1',
],
'type': 'nsExternalHelperAppService',
'constructor': 'nsExternalHelperAppService::GetSingleton',
'headers': ['nsExternalHelperAppService.h'],
'init_method': 'Init',
'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS,
},
{
'cid': '{56ebedd4-6ccf-48e8-bdae-adc77f044567}',
'contract_ids': [
@ -165,4 +152,34 @@ if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] == 'android':
'type': 'nsExternalURLHandlerService',
'headers': ['nsExternalURLHandlerService.h'],
},
# Android has its own externel-helper-app-service, so we omit
# that here for nsExternalHelperAppService.
{
'cid': '{a7f800e0-4306-11d4-98d0-001083010e9b}',
'contract_ids': [
'@mozilla.org/mime;1',
'@mozilla.org/uriloader/external-protocol-service;1',
],
'type': 'nsExternalHelperAppService',
'constructor': 'nsExternalHelperAppService::GetSingleton',
'headers': ['nsExternalHelperAppService.h'],
'init_method': 'Init',
'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS,
},
]
else:
Classes += [
{
'cid': '{a7f800e0-4306-11d4-98d0-001083010e9b}',
'contract_ids': [
'@mozilla.org/mime;1',
'@mozilla.org/uriloader/external-helper-app-service;1',
'@mozilla.org/uriloader/external-protocol-service;1',
],
'type': 'nsExternalHelperAppService',
'constructor': 'nsExternalHelperAppService::GetSingleton',
'headers': ['nsExternalHelperAppService.h'],
'init_method': 'Init',
'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS,
},
]

View File

@ -30,10 +30,6 @@ contract @mozilla.org/sharepicker;1 {1201d357-8417-4926-a694-e6408fbedcf8} proce
component {3d765750-1c3d-11ea-aaef-0800200c9a66} GeckoViewPrompt.js process=main
contract @mozilla.org/login-manager/prompter;1 {3d765750-1c3d-11ea-aaef-0800200c9a66} process=main
# GeckoViewExternalAppService.js
component {a89eeec6-6608-42ee-a4f8-04d425992f45} GeckoViewExternalAppService.js
contract @mozilla.org/uriloader/external-helper-app-service;1 {a89eeec6-6608-42ee-a4f8-04d425992f45}
# GeckoViewPush.js
component {a54d84d7-98a4-4fec-b664-e42e512ae9cc} GeckoViewPush.js
contract @mozilla.org/push/Service;1 {a54d84d7-98a4-4fec-b664-e42e512ae9cc}

View File

@ -0,0 +1,135 @@
/* -*- 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/. */
#include "GeckoViewExternalAppService.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/widget/EventDispatcher.h"
#include "mozilla/widget/nsWindow.h"
#include "JavaBuiltins.h"
static const char16_t kExternalResponseMessage[] =
u"GeckoView:ExternalResponse";
mozilla::StaticRefPtr<GeckoViewExternalAppService>
GeckoViewExternalAppService::sService;
/* static */
already_AddRefed<GeckoViewExternalAppService>
GeckoViewExternalAppService::GetSingleton() {
if (!sService) {
sService = new GeckoViewExternalAppService();
}
RefPtr<GeckoViewExternalAppService> service = sService;
return service.forget();
}
GeckoViewExternalAppService::GeckoViewExternalAppService() {}
NS_IMPL_ISUPPORTS(GeckoViewExternalAppService, nsIExternalHelperAppService);
NS_IMETHODIMP GeckoViewExternalAppService::DoContent(
const nsACString& aMimeContentType, nsIRequest* aRequest,
nsIInterfaceRequestor* aContentContext, bool aForceSave,
nsIInterfaceRequestor* aWindowContext,
nsIStreamListener** aStreamListener) {
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP GeckoViewExternalAppService::CreateListener(
const nsACString& aMimeContentType, nsIRequest* aRequest,
mozilla::dom::BrowsingContext* aContentContext, bool aForceSave,
nsIInterfaceRequestor* aWindowContext,
nsExternalAppHandler** aStreamListener) {
using namespace mozilla;
using namespace mozilla::dom;
MOZ_ASSERT(XRE_IsParentProcess());
// We currently never want to read the channel, so cancel it immediately.
aRequest->Cancel(NS_ERROR_ABORT);
nsresult rv;
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIWidget> widget =
aContentContext->Canonical()->GetParentProcessWidgetContaining();
if (!widget) {
return NS_ERROR_ABORT;
}
RefPtr<nsWindow> window = nsWindow::From(widget);
MOZ_ASSERT(window);
widget::EventDispatcher* dispatcher = window->GetEventDispatcher();
MOZ_ASSERT(dispatcher);
if (!dispatcher->HasListener(kExternalResponseMessage)) {
return NS_ERROR_ABORT;
}
AutoTArray<jni::String::LocalRef, 4> keys;
AutoTArray<jni::Object::LocalRef, 4> values;
nsCOMPtr<nsIURI> uri;
if (NS_WARN_IF(NS_FAILED(channel->GetURI(getter_AddRefs(uri))))) {
return NS_ERROR_ABORT;
}
nsAutoCString uriSpec;
if (NS_WARN_IF(NS_FAILED(uri->GetDisplaySpec(uriSpec)))) {
return NS_ERROR_ABORT;
}
keys.AppendElement(jni::StringParam(NS_LITERAL_STRING("uri")));
values.AppendElement(jni::StringParam(uriSpec));
nsCString contentType;
if (NS_WARN_IF(NS_FAILED(channel->GetContentType(contentType)))) {
return NS_ERROR_ABORT;
}
keys.AppendElement(jni::StringParam(NS_LITERAL_STRING("contentType")));
values.AppendElement(jni::StringParam(contentType));
int64_t contentLength = 0;
if (NS_WARN_IF(NS_FAILED(channel->GetContentLength(&contentLength)))) {
return NS_ERROR_ABORT;
}
keys.AppendElement(jni::StringParam(NS_LITERAL_STRING("contentLength")));
values.AppendElement(java::sdk::Integer::ValueOf(contentLength));
nsString filename;
if (NS_SUCCEEDED(channel->GetContentDispositionFilename(filename))) {
keys.AppendElement(jni::StringParam(NS_LITERAL_STRING("filename")));
values.AppendElement(jni::StringParam(filename));
}
auto bundleKeys = jni::ObjectArray::New<jni::String>(keys.Length());
auto bundleValues = jni::ObjectArray::New<jni::Object>(values.Length());
for (size_t i = 0; i < keys.Length(); ++i) {
bundleKeys->SetElement(i, keys[i]);
bundleValues->SetElement(i, values[i]);
}
auto bundle = java::GeckoBundle::New(bundleKeys, bundleValues);
Unused << NS_WARN_IF(
NS_FAILED(dispatcher->Dispatch(kExternalResponseMessage, bundle)));
return NS_ERROR_ABORT;
}
NS_IMETHODIMP GeckoViewExternalAppService::ApplyDecodingForExtension(
const nsACString& aExtension, const nsACString& aEncodingType,
bool* aApplyDecoding) {
// This currently doesn't matter, because we never read the stream.
*aApplyDecoding = true;
return NS_OK;
}

View File

@ -0,0 +1,26 @@
/* -*- 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 GeckoViewExternalAppService_h__
#define GeckoViewExternalAppService_h__
#include "nsIExternalHelperAppService.h"
#include "mozilla/StaticPtr.h"
class GeckoViewExternalAppService : public nsIExternalHelperAppService {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEXTERNALHELPERAPPSERVICE
GeckoViewExternalAppService();
static already_AddRefed<GeckoViewExternalAppService> GetSingleton();
private:
virtual ~GeckoViewExternalAppService() {}
static mozilla::StaticRefPtr<GeckoViewExternalAppService> sService;
};
#endif

View File

@ -1,65 +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/. */
"use strict";
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { GeckoViewUtils } = ChromeUtils.import(
"resource://gre/modules/GeckoViewUtils.jsm"
);
const { debug, warn } = GeckoViewUtils.initLogging("ExternalAppService"); // eslint-disable-line no-unused-vars
ChromeUtils.defineModuleGetter(
this,
"EventDispatcher",
"resource://gre/modules/Messaging.jsm"
);
function ExternalAppService() {
this.wrappedJSObject = this;
}
ExternalAppService.prototype = {
classID: Components.ID("{a89eeec6-6608-42ee-a4f8-04d425992f45}"),
QueryInterface: ChromeUtils.generateQI([Ci.nsIExternalHelperAppService]),
doContent(mimeType, request, context, forceSave) {
const channel = request.QueryInterface(Ci.nsIChannel);
debug`doContent: uri=${channel.URI.displaySpec}
contentType=${channel.contentType}`;
let filename = null;
try {
filename = channel.contentDispositionFilename;
} catch (e) {
// This throws NS_ERROR_NOT_AVAILABLE if there is not
// Content-disposition header.
}
GeckoViewUtils.getDispatcherForWindow(context).sendRequest({
type: "GeckoView:ExternalResponse",
uri: channel.URI.displaySpec,
contentType: channel.contentType,
contentLength: channel.contentLength,
filename,
});
request.cancel(Cr.NS_ERROR_ABORT);
Components.returnCode = Cr.NS_ERROR_ABORT;
},
applyDecodingForExtension(ext, encoding) {
debug`applyDecodingForExtension: extension=${ext}
encoding=${encoding}`;
// This doesn't matter for us right now because
// we shouldn't end up reading the stream.
return true;
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ExternalAppService]);

View File

@ -13,4 +13,14 @@ Classes = [
'headers': ['GeckoViewHistory.h'],
'constructor': 'GeckoViewHistory::GetSingleton',
},
{
'cid': '{91455c77-64a1-4c37-be00-f94eb9c7b8e1}',
'contract_ids': [
'@mozilla.org/uriloader/external-helper-app-service;1',
],
'type': 'GeckoViewExternalAppService',
'constructor': 'GeckoViewExternalAppService::GetSingleton',
'headers': ['GeckoViewExternalAppService.h'],
'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS,
},
]

View File

@ -4,6 +4,9 @@
# 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/.
SOURCES += ['GeckoViewExternalAppService.cpp']
EXPORTS += ['GeckoViewExternalAppService.h']
if CONFIG['MOZ_ANDROID_HISTORY']:
EXPORTS += [
'GeckoViewHistory.h',
@ -18,7 +21,6 @@ if CONFIG['MOZ_ANDROID_HISTORY']:
EXTRA_COMPONENTS += [
'GeckoView.manifest',
'GeckoViewExternalAppService.js',
'GeckoViewPermission.js',
'GeckoViewPrompt.js',
'GeckoViewPush.js',

View File

@ -215,7 +215,6 @@
#ifdef MOZ_GECKOVIEW_JAR
@BINPATH@/components/GeckoView.manifest
@BINPATH@/components/GeckoViewExternalAppService.js
@BINPATH@/components/GeckoViewPrompt.js
@BINPATH@/components/GeckoViewPush.js
@BINPATH@/components/GeckoViewPermission.js