Bug 1287717 - Part 2, close receiver page while loading fail. r=smaug.
MozReview-Commit-ID: Dogham2LmHG
--- a/dom/presentation/PresentationCallbacks.cpp
+++ b/dom/presentation/PresentationCallbacks.cpp
@@ -172,54 +172,56 @@ PresentationResponderLoadingCallback::In
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if ((busyFlags == nsIDocShell::BUSY_FLAGS_NONE) ||
(busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) {
// The docshell has finished loading or is receiving data (|STATE_TRANSFERRING|
// has already been fired), so the page is ready for presentation use.
- return NotifyReceiverReady();
+ return NotifyReceiverReady(/* isLoading = */ true);
}
// Start to listen to document state change event |STATE_TRANSFERRING|.
return mProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
}
nsresult
-PresentationResponderLoadingCallback::NotifyReceiverReady()
+PresentationResponderLoadingCallback::NotifyReceiverReady(bool aIsLoading)
{
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mProgress);
if (NS_WARN_IF(!window || !window->GetCurrentInnerWindow())) {
return NS_ERROR_NOT_AVAILABLE;
}
uint64_t windowId = window->GetCurrentInnerWindow()->WindowID();
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
- return service->NotifyReceiverReady(mSessionId, windowId);
+ return service->NotifyReceiverReady(mSessionId, windowId, aIsLoading);
}
// nsIWebProgressListener
NS_IMETHODIMP
PresentationResponderLoadingCallback::OnStateChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aStateFlags,
nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
- if (aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING) {
+ if (aStateFlags & (nsIWebProgressListener::STATE_TRANSFERRING |
+ nsIWebProgressListener::STATE_STOP)) {
mProgress->RemoveProgressListener(this);
- return NotifyReceiverReady();
+ bool isLoading = aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING;
+ return NotifyReceiverReady(isLoading);
}
return NS_OK;
}
NS_IMETHODIMP
PresentationResponderLoadingCallback::OnProgressChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
--- a/dom/presentation/PresentationCallbacks.h
+++ b/dom/presentation/PresentationCallbacks.h
@@ -71,17 +71,17 @@ public:
explicit PresentationResponderLoadingCallback(const nsAString& aSessionId);
nsresult Init(nsIDocShell* aDocShell);
private:
~PresentationResponderLoadingCallback();
- nsresult NotifyReceiverReady();
+ nsresult NotifyReceiverReady(bool aIsLoading);
nsString mSessionId;
nsCOMPtr<nsIWebProgress> mProgress;
};
} // namespace dom
} // namespace mozilla
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -884,26 +884,31 @@ NS_IMETHODIMP
PresentationService::GetExistentSessionIdAtLaunch(uint64_t aWindowId,
nsAString& aSessionId)
{
return GetExistentSessionIdAtLaunchInternal(aWindowId, aSessionId);
}
NS_IMETHODIMP
PresentationService::NotifyReceiverReady(const nsAString& aSessionId,
- uint64_t aWindowId)
+ uint64_t aWindowId,
+ bool aIsLoading)
{
RefPtr<PresentationSessionInfo> info =
GetSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER);
if (NS_WARN_IF(!info)) {
return NS_ERROR_NOT_AVAILABLE;
}
AddRespondingSessionId(aWindowId, aSessionId);
+ if (!aIsLoading) {
+ return static_cast<PresentationPresentingInfo*>(info.get())->NotifyResponderFailure();
+ }
+
nsCOMPtr<nsIPresentationRespondingListener> listener;
if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) {
nsresult rv = listener->NotifySessionConnect(aWindowId, aSessionId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -1210,16 +1210,27 @@ PresentationPresentingInfo::NotifyRespon
if (NS_WARN_IF(NS_FAILED(rv))) {
return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
}
}
return NS_OK;
}
+nsresult
+PresentationPresentingInfo::NotifyResponderFailure()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
+}
+
// nsIPresentationControlChannelListener
NS_IMETHODIMP
PresentationPresentingInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
{
if (NS_WARN_IF(mHasFlushPendingEvents)) {
return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
}
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -225,16 +225,17 @@ public:
{
MOZ_ASSERT(aDevice);
SetDevice(aDevice);
}
nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
nsresult NotifyResponderReady();
+ nsresult NotifyResponderFailure();
NS_IMETHODIMP OnSessionTransport(nsIPresentationSessionTransport* transport) override;
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void SetPromise(Promise* aPromise)
--- a/dom/presentation/interfaces/nsIPresentationService.idl
+++ b/dom/presentation/interfaces/nsIPresentationService.idl
@@ -169,24 +169,24 @@ interface nsIPresentationService : nsISu
*
* @param windowId: The inner window ID used to look up the session ID.
*/
DOMString getExistentSessionIdAtLaunch(in unsigned long long windowId);
/*
* Notify the receiver page is ready for presentation use.
*
- * @param sessionId: An ID to identify presentation session.
- * @param windowId: The inner window ID associated with the presentation
- * session. (0 implies no window ID since no actual window
- * uses 0 as its ID. Generally it's the case the window is
- * located in different process from this service)
+ * @param sessionId An ID to identify presentation session.
+ * @param windowId The inner window ID associated with the presentation
+ * session.
+ * @param isLoading true if receiver page is loading successfully.
*/
void notifyReceiverReady(in DOMString sessionId,
- [optional] in unsigned long long windowId);
+ in unsigned long long windowId,
+ in boolean isLoading);
/*
* Notify the transport is closed
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param reason: the error message. NS_OK indicates it is closed normally.
*/
--- a/dom/presentation/ipc/PPresentation.ipdl
+++ b/dom/presentation/ipc/PPresentation.ipdl
@@ -90,14 +90,14 @@ parent:
async RegisterSessionHandler(nsString aSessionId, uint8_t aRole);
async UnregisterSessionHandler(nsString aSessionId, uint8_t aRole);
async RegisterRespondingHandler(uint64_t aWindowId);
async UnregisterRespondingHandler(uint64_t aWindowId);
async PPresentationRequest(PresentationIPCRequest aRequest);
- async NotifyReceiverReady(nsString aSessionId, uint64_t aWindowId);
+ async NotifyReceiverReady(nsString aSessionId, uint64_t aWindowId, bool aIsLoading);
async NotifyTransportClosed(nsString aSessionId, uint8_t aRole, nsresult aReason);
};
} // namespace dom
} // namespace mozilla
--- a/dom/presentation/ipc/PresentationIPCService.cpp
+++ b/dom/presentation/ipc/PresentationIPCService.cpp
@@ -359,30 +359,32 @@ NS_IMETHODIMP
PresentationIPCService::GetExistentSessionIdAtLaunch(uint64_t aWindowId,
nsAString& aSessionId)
{
return GetExistentSessionIdAtLaunchInternal(aWindowId, aSessionId);;
}
NS_IMETHODIMP
PresentationIPCService::NotifyReceiverReady(const nsAString& aSessionId,
- uint64_t aWindowId)
+ uint64_t aWindowId,
+ bool aIsLoading)
{
MOZ_ASSERT(NS_IsMainThread());
// No actual window uses 0 as its ID.
if (NS_WARN_IF(aWindowId == 0)) {
return NS_ERROR_NOT_AVAILABLE;
}
// Track the responding info for an OOP receiver page.
AddRespondingSessionId(aWindowId, aSessionId);
NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsString(aSessionId),
- aWindowId));
+ aWindowId,
+ aIsLoading));
// Release mCallback after using aSessionId
// because aSessionId is held by mCallback.
mCallback = nullptr;
return NS_OK;
}
NS_IMETHODIMP
--- a/dom/presentation/ipc/PresentationParent.cpp
+++ b/dom/presentation/ipc/PresentationParent.cpp
@@ -275,22 +275,25 @@ PresentationParent::NotifySessionConnect
!SendNotifySessionConnect(aWindowId, nsString(aSessionId)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
PresentationParent::RecvNotifyReceiverReady(const nsString& aSessionId,
- const uint64_t& aWindowId)
+ const uint64_t& aWindowId,
+ const bool& aIsLoading)
{
MOZ_ASSERT(mService);
RegisterTransportBuilder(aSessionId, nsIPresentationService::ROLE_RECEIVER);
- NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId, aWindowId)));
+ NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId,
+ aWindowId,
+ aIsLoading)));
return true;
}
bool
PresentationParent::RecvNotifyTransportClosed(const nsString& aSessionId,
const uint8_t& aRole,
const nsresult& aReason)
{
--- a/dom/presentation/ipc/PresentationParent.h
+++ b/dom/presentation/ipc/PresentationParent.h
@@ -66,17 +66,18 @@ public:
virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId,
const uint8_t& aRole) override;
virtual bool RecvRegisterRespondingHandler(const uint64_t& aWindowId) override;
virtual bool RecvUnregisterRespondingHandler(const uint64_t& aWindowId) override;
virtual bool RecvNotifyReceiverReady(const nsString& aSessionId,
- const uint64_t& aWindowId) override;
+ const uint64_t& aWindowId,
+ const bool& aIsLoading) override;
virtual bool RecvNotifyTransportClosed(const nsString& aSessionId,
const uint8_t& aRole,
const nsresult& aReason) override;
private:
virtual ~PresentationParent();
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test
@@ -0,0 +1,1 @@
+
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/unknown
--- a/dom/presentation/tests/mochitest/mochitest.ini
+++ b/dom/presentation/tests/mochitest/mochitest.ini
@@ -15,16 +15,19 @@ support-files =
file_presentation_receiver_auxiliary_navigation.html
test_presentation_receiver_auxiliary_navigation.js
file_presentation_sandboxed_presentation.html
file_presentation_terminate.html
test_presentation_terminate.js
file_presentation_terminate_establish_connection_error.html
test_presentation_terminate_establish_connection_error.js
file_presentation_reconnect.html
+ file_presentation_unknown_content_type.test
+ file_presentation_unknown_content_type.test^headers^
+ test_presentation_tcp_receiver_establish_connection_unknown_content_type.js
[test_presentation_dc_sender.html]
[test_presentation_dc_receiver.html]
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
[test_presentation_dc_receiver_oop.html]
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
[test_presentation_1ua_sender_and_receiver.html]
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
@@ -38,16 +41,20 @@ skip-if = (e10s || toolkit == 'gonk' ||
[test_presentation_tcp_sender_disconnect.html]
skip-if = toolkit == 'android' # Bug 1129785
[test_presentation_tcp_sender_establish_connection_error.html]
skip-if = toolkit == 'android' # Bug 1129785
[test_presentation_tcp_receiver_establish_connection_error.html]
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android' || os == 'mac' || os == 'win' || buildapp == 'mulet') # Bug 1129785, Bug 1204709
[test_presentation_tcp_receiver_establish_connection_timeout.html]
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
+[test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html]
+skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android')
+[test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html]
+skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android')
[test_presentation_tcp_receiver.html]
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
[test_presentation_tcp_receiver_oop.html]
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
[test_presentation_receiver_auxiliary_navigation_inproc.html]
skip-if = (e10s || toolkit == 'gonk')
[test_presentation_receiver_auxiliary_navigation_oop.html]
skip-if = (e10s || toolkit == 'gonk')
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type.js
@@ -0,0 +1,86 @@
+'use strict';
+
+var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
+var receiverUrl = SimpleTest.getTestFileURL('file_presentation_unknown_content_type.test');
+
+var obs = SpecialPowers.Cc['@mozilla.org/observer-service;1']
+ .getService(SpecialPowers.Ci.nsIObserverService);
+
+var receiverIframe;
+
+function setup() {
+ return new Promise(function(aResolve, aReject) {
+ gScript.sendAsyncMessage('trigger-device-add');
+
+ receiverIframe = document.createElement('iframe');
+ receiverIframe.setAttribute('mozbrowser', 'true');
+ receiverIframe.setAttribute('mozpresentation', receiverUrl);
+ receiverIframe.setAttribute('src', receiverUrl);
+ var oop = location.pathname.indexOf('_inproc') == -1;
+ receiverIframe.setAttribute("remote", oop);
+
+ var promise = new Promise(function(aResolve, aReject) {
+ document.body.appendChild(receiverIframe);
+
+ aResolve(receiverIframe);
+ });
+ obs.notifyObservers(promise, 'setup-request-promise', null);
+
+ aResolve();
+ });
+}
+
+function testIncomingSessionRequestReceiverLaunchUnknownContentType() {
+ let promise = Promise.all([
+ new Promise(function(aResolve, aReject) {
+ gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) {
+ gScript.removeMessageListener('receiver-launching', launchReceiverHandler);
+ info('Trying to launch receiver page.');
+
+ receiverIframe.addEventListener('mozbrowserclose', function() {
+ ok(true, 'observe receiver window closed');
+ aResolve();
+ });
+ });
+ }),
+ new Promise(function(aResolve, aReject) {
+ gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
+ gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
+ is(aReason, 0x80530020 /* NS_ERROR_DOM_OPERATION_ERR */, 'The control channel is closed due to load failure.');
+ aResolve();
+ });
+ })
+ ]);
+
+ gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl);
+ return promise;
+}
+
+function teardown() {
+ gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
+ gScript.removeMessageListener('teardown-complete', teardownCompleteHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+
+ gScript.sendAsyncMessage('teardown');
+}
+
+function runTests() {
+ setup().
+ then(testIncomingSessionRequestReceiverLaunchUnknownContentType).
+ then(teardown);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPermissions([
+ {type: 'presentation-device-manage', allow: false, context: document},
+ {type: 'presentation', allow: true, context: document},
+ {type: 'browser', allow: true, context: document},
+], function() {
+ SpecialPowers.pushPrefEnv({ 'set': [['dom.presentation.enabled', true],
+ ['dom.presentation.session_transport.data_channel.enable', false],
+ ['dom.mozBrowserFramesEnabled', true],
+ ['dom.ipc.tabs.disabled', false]]},
+ runTests);
+});
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<head>
+ <meta charset="utf-8">
+ <title>Test for unknown content type of B2G Presentation API at receiver side</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287717">Test for unknown content type of B2G Presentation API at receiver side</a>
+ <script type="application/javascript;version=1.8" src="test_presentation_tcp_receiver_establish_connection_unknown_content_type.js">
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<head>
+ <meta charset="utf-8">
+ <title>Test for unknown content type of B2G Presentation API at receiver side (OOP)</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287717">Test for unknown content type of B2G Presentation API at receiver side (OOP)</a>
+ <script type="application/javascript;version=1.8" src="test_presentation_tcp_receiver_establish_connection_unknown_content_type.js">
+ </script>
+</body>
+</html>