import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

33
uriloader/base/moz.build Normal file
View file

@ -0,0 +1,33 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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('/ipc/chromium/chromium-config.mozbuild')
XPIDL_SOURCES += [
'nsCURILoader.idl',
'nsIContentHandler.idl',
'nsIDocumentLoader.idl',
'nsITransfer.idl',
'nsIURIContentListener.idl',
'nsIURILoader.idl',
'nsIWebProgress.idl',
'nsIWebProgressListener.idl',
'nsIWebProgressListener2.idl',
]
XPIDL_MODULE = 'uriloader'
EXPORTS += [
'nsDocLoader.h',
'nsURILoader.h',
]
UNIFIED_SOURCES += [
'nsDocLoader.cpp',
'nsURILoader.cpp',
]
FINAL_LIBRARY = 'xul'

View file

@ -0,0 +1,49 @@
/* -*- Mode: IDL; tab-width: 3; 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 "nsIURILoader.idl"
/*
nsCURILoader implements:
-------------------------
nsIURILoader
*/
%{ C++
// {9F6D5D40-90E7-11d3-AF93-00A024FFC08C} -
#define NS_URI_LOADER_CID \
{ 0x9f6d5d40, 0x90e7, 0x11d3, { 0xaf, 0x80, 0x00, 0xa0, 0x24, 0xff, 0xc0, 0x8c } }
#define NS_URI_LOADER_CONTRACTID \
"@mozilla.org/uriloader;1"
/* 057b04d0-0ccf-11d2-beba-00805f8a66dc */
#define NS_DOCUMENTLOADER_SERVICE_CID \
{ 0x057b04d0, 0x0ccf, 0x11d2,{0xbe, 0xba, 0x00, 0x80, 0x5f, 0x8a, 0x66, 0xdc}}
#define NS_DOCUMENTLOADER_SERVICE_CONTRACTID \
"@mozilla.org/docloaderservice;1"
#define NS_CONTENT_HANDLER_CONTRACTID "@mozilla.org/uriloader/content-handler;1"
#define NS_CONTENT_HANDLER_CONTRACTID_PREFIX NS_CONTENT_HANDLER_CONTRACTID "?type="
/**
* A category where content listeners can register. The name of the entry must
* be the content that this listener wants to handle, the value must be a
* contract ID for the listener. It will be created using createInstance (not
* getService).
*
* Listeners added this way are tried after the initial target of the load and
* after explicitly registered listeners (nsIURILoader::registerContentListener).
*
* These listeners must implement at least nsIURIContentListener (and
* nsISupports).
*
* @see nsICategoryManager
* @see nsIURIContentListener
*/
#define NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY "external-uricontentlisteners"
%}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,335 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 nsDocLoader_h__
#define nsDocLoader_h__
#include "nsIDocumentLoader.h"
#include "nsIWebProgress.h"
#include "nsIWebProgressListener.h"
#include "nsIRequestObserver.h"
#include "nsWeakReference.h"
#include "nsILoadGroup.h"
#include "nsCOMArray.h"
#include "nsTObserverArray.h"
#include "nsString.h"
#include "nsIChannel.h"
#include "nsIProgressEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIChannelEventSink.h"
#include "nsISecurityEventSink.h"
#include "nsISupportsPriority.h"
#include "nsCOMPtr.h"
#include "PLDHashTable.h"
#include "nsAutoPtr.h"
#include "mozilla/LinkedList.h"
/****************************************************************************
* nsDocLoader implementation...
****************************************************************************/
#define NS_THIS_DOCLOADER_IMPL_CID \
{ /* b4ec8387-98aa-4c08-93b6-6d23069c06f2 */ \
0xb4ec8387, \
0x98aa, \
0x4c08, \
{0x93, 0xb6, 0x6d, 0x23, 0x06, 0x9c, 0x06, 0xf2} \
}
class nsDocLoader : public nsIDocumentLoader,
public nsIRequestObserver,
public nsSupportsWeakReference,
public nsIProgressEventSink,
public nsIWebProgress,
public nsIInterfaceRequestor,
public nsIChannelEventSink,
public nsISecurityEventSink,
public nsISupportsPriority
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_THIS_DOCLOADER_IMPL_CID)
nsDocLoader();
virtual MOZ_MUST_USE nsresult Init();
static already_AddRefed<nsDocLoader> GetAsDocLoader(nsISupports* aSupports);
// Needed to deal with ambiguous inheritance from nsISupports...
static nsISupports* GetAsSupports(nsDocLoader* aDocLoader) {
return static_cast<nsIDocumentLoader*>(aDocLoader);
}
// Add aDocLoader as a child to the docloader service.
static MOZ_MUST_USE nsresult AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader);
NS_DECL_ISUPPORTS
NS_DECL_NSIDOCUMENTLOADER
// nsIProgressEventSink
NS_DECL_NSIPROGRESSEVENTSINK
NS_DECL_NSISECURITYEVENTSINK
// nsIRequestObserver methods: (for observing the load group)
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSIWEBPROGRESS
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSISUPPORTSPRIORITY
// Implementation specific methods...
// Remove aChild from our childlist. This nulls out the child's mParent
// pointer.
MOZ_MUST_USE nsresult RemoveChildLoader(nsDocLoader *aChild);
// Add aChild to our child list. This will set aChild's mParent pointer to
// |this|.
MOZ_MUST_USE nsresult AddChildLoader(nsDocLoader* aChild);
nsDocLoader* GetParent() const { return mParent; }
struct nsListenerInfo {
nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask)
: mWeakListener(aListener),
mNotifyMask(aNotifyMask)
{
}
// Weak pointer for the nsIWebProgressListener...
nsWeakPtr mWeakListener;
// Mask indicating which notifications the listener wants to receive.
unsigned long mNotifyMask;
};
protected:
virtual ~nsDocLoader();
virtual MOZ_MUST_USE nsresult SetDocLoaderParent(nsDocLoader * aLoader);
bool IsBusy();
void Destroy();
virtual void DestroyChildren();
nsIDocumentLoader* ChildAt(int32_t i) {
return mChildList.SafeElementAt(i, nullptr);
}
void FireOnProgressChange(nsDocLoader* aLoadInitiator,
nsIRequest *request,
int64_t aProgress,
int64_t aProgressMax,
int64_t aProgressDelta,
int64_t aTotalProgress,
int64_t aMaxTotalProgress);
// This should be at least 2 long since we'll generally always
// have the current page and the global docloader on the ancestor
// list. But to deal with frames it's better to make it a bit
// longer, and it's always a stack temporary so there's no real
// reason not to.
typedef AutoTArray<RefPtr<nsDocLoader>, 8> WebProgressList;
void GatherAncestorWebProgresses(WebProgressList& aList);
void FireOnStateChange(nsIWebProgress *aProgress,
nsIRequest* request,
int32_t aStateFlags,
nsresult aStatus);
// The guts of FireOnStateChange, but does not call itself on our ancestors.
// The arguments that are const are const so that we can detect cases when
// DoFireOnStateChange wants to propagate changes to the next web progress
// at compile time. The ones that are not, are references so that such
// changes can be propagated.
void DoFireOnStateChange(nsIWebProgress * const aProgress,
nsIRequest* const request,
int32_t &aStateFlags,
const nsresult aStatus);
void FireOnStatusChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest,
nsresult aStatus,
const char16_t* aMessage);
void FireOnLocationChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
nsIURI *aUri,
uint32_t aFlags);
MOZ_MUST_USE bool RefreshAttempted(nsIWebProgress* aWebProgress,
nsIURI *aURI,
int32_t aDelay,
bool aSameURI);
// this function is overridden by the docshell, it is provided so that we
// can pass more information about redirect state (the normal OnStateChange
// doesn't get the new channel).
// @param aRedirectFlags The flags being sent to OnStateChange that
// indicate the type of redirect.
// @param aStateFlags The channel flags normally sent to OnStateChange.
virtual void OnRedirectStateChange(nsIChannel* aOldChannel,
nsIChannel* aNewChannel,
uint32_t aRedirectFlags,
uint32_t aStateFlags) {}
void doStartDocumentLoad();
void doStartURLLoad(nsIRequest *request);
void doStopURLLoad(nsIRequest *request, nsresult aStatus);
void doStopDocumentLoad(nsIRequest *request, nsresult aStatus);
// Inform a parent docloader that aChild is about to call its onload
// handler.
MOZ_MUST_USE bool ChildEnteringOnload(nsIDocumentLoader* aChild) {
// It's ok if we're already in the list -- we'll just be in there twice
// and then the RemoveObject calls from ChildDoneWithOnload will remove
// us.
return mChildrenInOnload.AppendObject(aChild);
}
// Inform a parent docloader that aChild is done calling its onload
// handler.
void ChildDoneWithOnload(nsIDocumentLoader* aChild) {
mChildrenInOnload.RemoveObject(aChild);
DocLoaderIsEmpty(true);
}
protected:
struct nsStatusInfo : public mozilla::LinkedListElement<nsStatusInfo>
{
nsString mStatusMessage;
nsresult mStatusCode;
// Weak mRequest is ok; we'll be told if it decides to go away.
nsIRequest * const mRequest;
explicit nsStatusInfo(nsIRequest* aRequest) :
mRequest(aRequest)
{
MOZ_COUNT_CTOR(nsStatusInfo);
}
~nsStatusInfo()
{
MOZ_COUNT_DTOR(nsStatusInfo);
}
};
struct nsRequestInfo : public PLDHashEntryHdr
{
explicit nsRequestInfo(const void* key)
: mKey(key), mCurrentProgress(0), mMaxProgress(0), mUploading(false)
, mLastStatus(nullptr)
{
MOZ_COUNT_CTOR(nsRequestInfo);
}
~nsRequestInfo()
{
MOZ_COUNT_DTOR(nsRequestInfo);
}
nsIRequest* Request() {
return static_cast<nsIRequest*>(const_cast<void*>(mKey));
}
const void* mKey; // Must be first for the PLDHashTable stubs to work
int64_t mCurrentProgress;
int64_t mMaxProgress;
bool mUploading;
nsAutoPtr<nsStatusInfo> mLastStatus;
};
static void RequestInfoHashInitEntry(PLDHashEntryHdr* entry, const void* key);
static void RequestInfoHashClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry);
// IMPORTANT: The ownership implicit in the following member
// variables has been explicitly checked and set using nsCOMPtr
// for owning pointers and raw COM interface pointers for weak
// (ie, non owning) references. If you add any members to this
// class, please make the ownership explicit (pinkerton, scc).
nsCOMPtr<nsIRequest> mDocumentRequest; // [OWNER] ???compare with document
nsDocLoader* mParent; // [WEAK]
typedef nsAutoTObserverArray<nsListenerInfo, 8> ListenerArray;
ListenerArray mListenerInfoList;
nsCOMPtr<nsILoadGroup> mLoadGroup;
// We hold weak refs to all our kids
nsTObserverArray<nsDocLoader*> mChildList;
// The following member variables are related to the new nsIWebProgress
// feedback interfaces that travis cooked up.
int32_t mProgressStateFlags;
int64_t mCurrentSelfProgress;
int64_t mMaxSelfProgress;
int64_t mCurrentTotalProgress;
int64_t mMaxTotalProgress;
PLDHashTable mRequestInfoHash;
int64_t mCompletedTotalProgress;
mozilla::LinkedList<nsStatusInfo> mStatusInfoList;
/*
* This flag indicates that the loader is loading a document. It is set
* from the call to LoadDocument(...) until the OnConnectionsComplete(...)
* notification is fired...
*/
bool mIsLoadingDocument;
/* Flag to indicate that we're in the process of restoring a document. */
bool mIsRestoringDocument;
/* Flag to indicate that we're in the process of flushing layout
under DocLoaderIsEmpty() and should not do another flush. */
bool mDontFlushLayout;
/* Flag to indicate whether we should consider ourselves as currently
flushing layout for the purposes of IsBusy. For example, if Stop has
been called then IsBusy should return false even if we are still
flushing. */
bool mIsFlushingLayout;
private:
static const PLDHashTableOps sRequestInfoHashOps;
// A list of kids that are in the middle of their onload calls and will let
// us know once they're done. We don't want to fire onload for "normal"
// DocLoaderIsEmpty calls (those coming from requests finishing in our
// loadgroup) unless this is empty.
nsCOMArray<nsIDocumentLoader> mChildrenInOnload;
// DocLoaderIsEmpty should be called whenever the docloader may be empty.
// This method is idempotent and does nothing if the docloader is not in
// fact empty. This method _does_ make sure that layout is flushed if our
// loadgroup has no active requests before checking for "real" emptiness if
// aFlushLayout is true.
void DocLoaderIsEmpty(bool aFlushLayout);
int64_t GetMaxTotalProgress();
nsresult AddRequestInfo(nsIRequest* aRequest);
void RemoveRequestInfo(nsIRequest* aRequest);
nsRequestInfo *GetRequestInfo(nsIRequest* aRequest);
void ClearRequestInfoHash();
int64_t CalculateMaxProgress();
/// void DumpChannelInfo(void);
// used to clear our internal progress state between loads...
void ClearInternalProgress();
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsDocLoader, NS_THIS_DOCLOADER_IMPL_CID)
#endif /* nsDocLoader_h__ */

View file

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
interface nsIRequest;
interface nsIInterfaceRequestor;
[scriptable, uuid(49439df2-b3d2-441c-bf62-866bdaf56fd2)]
interface nsIContentHandler : nsISupports
{
/**
* Tells the content handler to take over handling the content. If this
* function succeeds, the URI Loader will leave this request alone, ignoring
* progress notifications. Failure of this method will cause the request to be
* cancelled, unless the error code is NS_ERROR_WONT_HANDLE_CONTENT (see
* below).
*
* @param aWindowContext
* Window context, used to get things like the current nsIDOMWindow
* for this request. May be null.
* @param aContentType
* The content type of aRequest
* @param aRequest
* A request whose content type is already known.
*
* @throw NS_ERROR_WONT_HANDLE_CONTENT Indicates that this handler does not
* want to handle this content. A different way for handling this
* content should be tried.
*/
void handleContent(in string aContentType,
in nsIInterfaceRequestor aWindowContext,
in nsIRequest aRequest);
};

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
interface nsILoadGroup;
interface nsIChannel;
interface nsIURI;
interface nsIWebProgress;
interface nsIRequest;
/**
* An nsIDocumentLoader is an interface responsible for tracking groups of
* loads that belong together (images, external scripts, etc) and subdocuments
* (<iframe>, <frame>, etc). It is also responsible for sending
* nsIWebProgressListener notifications.
* XXXbz this interface should go away, we think...
*/
[scriptable, uuid(bbe961ee-59e9-42bb-be50-0331979bb79f)]
interface nsIDocumentLoader : nsISupports
{
// Stop all loads in the loadgroup of this docloader
void stop();
// XXXbz is this needed? For embedding? What this does is does is not
// defined by this interface!
readonly attribute nsISupports container;
// The loadgroup associated with this docloader
readonly attribute nsILoadGroup loadGroup;
// The defaultLoadRequest of the loadgroup associated with this docloader
readonly attribute nsIChannel documentChannel;
};

View file

@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsIWebProgressListener2.idl"
interface nsIArray;
interface nsIURI;
interface nsICancelable;
interface nsIMIMEInfo;
interface nsIFile;
[scriptable, uuid(37ec75d3-97ad-4da8-afaa-eabe5b4afd73)]
interface nsITransfer : nsIWebProgressListener2 {
/**
* Initializes the transfer with certain properties. This function must
* be called prior to accessing any properties on this interface.
*
* @param aSource The source URI of the transfer. Must not be null.
*
* @param aTarget The target URI of the transfer. Must not be null.
*
* @param aDisplayName The user-readable description of the transfer.
* Can be empty.
*
* @param aMIMEInfo The MIME info associated with the target,
* including MIME type and helper app when appropriate.
* This parameter is optional.
*
* @param startTime Time when the download started (ie, when the first
* response from the server was received)
* XXX presumably wbp and exthandler do this differently
*
* @param aTempFile The location of a temporary file; i.e. a file in which
* the received data will be stored, but which is not
* equal to the target file. (will be moved to the real
* target by the caller, when the download is finished)
* May be null.
*
* @param aCancelable An object that can be used to abort the download.
* Must not be null.
* Implementations are expected to hold a strong
* reference to this object until the download is
* finished, at which point they should release the
* reference.
*
* @param aIsPrivate Used to determine the privacy status of the new transfer.
* If true, indicates that the transfer was initiated from
* a source that desires privacy.
*/
void init(in nsIURI aSource,
in nsIURI aTarget,
in AString aDisplayName,
in nsIMIMEInfo aMIMEInfo,
in PRTime startTime,
in nsIFile aTempFile,
in nsICancelable aCancelable,
in boolean aIsPrivate);
/*
* Used to notify the transfer object of the hash of the downloaded file.
* Must be called on the main thread, only after the download has finished
* successfully.
* @param aHash The SHA-256 hash in raw bytes of the downloaded file.
*/
void setSha256Hash(in ACString aHash);
/*
* Used to notify the transfer object of the signature of the downloaded
* file. Must be called on the main thread, only after the download has
* finished successfully.
* @param aSignatureInfo The nsIArray of nsIX509CertList of nsIX509Cert
* certificates of the downloaded file.
*/
void setSignatureInfo(in nsIArray aSignatureInfo);
/*
* Used to notify the transfer object of the redirects associated with the
* channel that terminated in the downloaded file. Must be called on the
* main thread, only after the download has finished successfully.
* @param aRedirects The nsIArray of nsIPrincipal of redirected URIs
* associated with the downloaded file.
*/
void setRedirects(in nsIArray aRedirects);
};
%{C++
/**
* A component with this contract ID will be created each time a download is
* started, and nsITransfer::Init will be called on it and an observer will be set.
*
* Notifications of the download progress will happen via
* nsIWebProgressListener/nsIWebProgressListener2.
*
* INTERFACES THAT MUST BE IMPLEMENTED:
* nsITransfer
* nsIWebProgressListener
* nsIWebProgressListener2
*
* XXX move this to nsEmbedCID.h once the interfaces (and the contract ID) are
* frozen.
*/
#define NS_TRANSFER_CONTRACTID "@mozilla.org/transfer;1"
%}

View file

@ -0,0 +1,135 @@
/* -*- Mode: IDL; 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 "nsISupports.idl"
interface nsIRequest;
interface nsIStreamListener;
interface nsIURI;
/**
* nsIURIContentListener is an interface used by components which
* want to know (and have a chance to handle) a particular content type.
* Typical usage scenarios will include running applications which register
* a nsIURIContentListener for each of its content windows with the uri
* dispatcher service.
*/
[scriptable, uuid(10a28f38-32e8-4c63-8aa1-12eaaebc369a)]
interface nsIURIContentListener : nsISupports
{
/**
* Gives the original content listener first crack at stopping a load before
* it happens.
*
* @param aURI URI that is being opened.
*
* @return <code>false</code> if the load can continue;
* <code>true</code> if the open should be aborted.
*/
boolean onStartURIOpen(in nsIURI aURI);
/**
* Notifies the content listener to hook up an nsIStreamListener capable of
* consuming the data stream.
*
* @param aContentType Content type of the data.
* @param aIsContentPreferred Indicates whether the content should be
* preferred by this listener.
* @param aRequest Request that is providing the data.
* @param aContentHandler nsIStreamListener that will consume the data.
* This should be set to <code>nullptr</code> if
* this content listener can't handle the content
* type; in this case, doContent should also fail
* (i.e., return failure nsresult).
*
* @return <code>true</code> if the load should
* be aborted and consumer wants to
* handle the load completely by itself. This
* causes the URI Loader do nothing else...
* <code>false</code> if the URI Loader should
* continue handling the load and call the
* returned streamlistener's methods.
*/
boolean doContent(in ACString aContentType,
in boolean aIsContentPreferred,
in nsIRequest aRequest,
out nsIStreamListener aContentHandler);
/**
* When given a uri to dispatch, if the URI is specified as 'preferred
* content' then the uri loader tries to find a preferred content handler
* for the content type. The thought is that many content listeners may
* be able to handle the same content type if they have to. i.e. the mail
* content window can handle text/html just like a browser window content
* listener. However, if the user clicks on a link with text/html content,
* then the browser window should handle that content and not the mail
* window where the user may have clicked the link. This is the difference
* between isPreferred and canHandleContent.
*
* @param aContentType Content type of the data.
* @param aDesiredContentType Indicates that aContentType must be converted
* to aDesiredContentType before processing the
* data. This causes a stream converted to be
* inserted into the nsIStreamListener chain.
* This argument can be <code>nullptr</code> if
* the content should be consumed directly as
* aContentType.
*
* @return <code>true</code> if this is a preferred
* content handler for aContentType;
* <code>false<code> otherwise.
*/
boolean isPreferred(in string aContentType, out string aDesiredContentType);
/**
* When given a uri to dispatch, if the URI is not specified as 'preferred
* content' then the uri loader calls canHandleContent to see if the content
* listener is capable of handling the content.
*
* @param aContentType Content type of the data.
* @param aIsContentPreferred Indicates whether the content should be
* preferred by this listener.
* @param aDesiredContentType Indicates that aContentType must be converted
* to aDesiredContentType before processing the
* data. This causes a stream converted to be
* inserted into the nsIStreamListener chain.
* This argument can be <code>nullptr</code> if
* the content should be consumed directly as
* aContentType.
*
* @return <code>true</code> if the data can be consumed.
* <code>false</code> otherwise.
*
* Note: I really envision canHandleContent as a method implemented
* by the docshell as the implementation is generic to all doc
* shells. The isPreferred decision is a decision made by a top level
* application content listener that sits at the top of the docshell
* hierarchy.
*/
boolean canHandleContent(in string aContentType,
in boolean aIsContentPreferred,
out string aDesiredContentType);
/**
* The load context associated with a particular content listener.
* The URI Loader stores and accesses this value as needed.
*/
attribute nsISupports loadCookie;
/**
* The parent content listener if this particular listener is part of a chain
* of content listeners (i.e. a docshell!)
*
* @note If this attribute is set to an object that implements
* nsISupportsWeakReference, the implementation should get the
* nsIWeakReference and hold that. Otherwise, the implementation
* should not refcount this interface; it should assume that a non
* null value is always valid. In that case, the caller is
* responsible for explicitly setting this value back to null if the
* parent content listener is destroyed.
*/
attribute nsIURIContentListener parentContentListener;
};

View file

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
interface nsIURIContentListener;
interface nsIURI;
interface nsILoadGroup;
interface nsIProgressEventSink;
interface nsIChannel;
interface nsIRequest;
interface nsIStreamListener;
interface nsIInputStream;
interface nsIInterfaceRequestor;
/**
* The uri dispatcher is responsible for taking uri's, determining
* the content and routing the opened url to the correct content
* handler.
*
* When you encounter a url you want to open, you typically call
* openURI, passing it the content listener for the window the uri is
* originating from. The uri dispatcher opens the url to discover the
* content type. It then gives the content listener first crack at
* handling the content. If it doesn't want it, the dispatcher tries
* to hand it off one of the registered content listeners. This allows
* running applications the chance to jump in and handle the content.
*
* If that also fails, then the uri dispatcher goes to the registry
* looking for the preferred content handler for the content type
* of the uri. The content handler may create an app instance
* or it may hand the contents off to a platform specific plugin
* or helper app. Or it may hand the url off to an OS registered
* application.
*/
[scriptable, uuid(8762c4e7-be35-4958-9b81-a05685bb516d)]
interface nsIURILoader : nsISupports
{
/**
* @name Flags for opening URIs.
*/
/* @{ */
/**
* Should the content be displayed in a container that prefers the
* content-type, or will any container do.
*/
const unsigned long IS_CONTENT_PREFERRED = 1 << 0;
/**
* If this flag is set, only the listener of the specified window context will
* be considered for content handling; if it refuses the load, an error will
* be indicated.
*/
const unsigned long DONT_RETARGET = 1 << 1;
/* @} */
/**
* As applications such as messenger and the browser are instantiated,
* they register content listener's with the uri dispatcher corresponding
* to content windows within that application.
*
* Note to self: we may want to optimize things a bit more by requiring
* the content types the registered content listener cares about.
*
* @param aContentListener
* The listener to register. This listener must implement
* nsISupportsWeakReference.
*
* @see the nsIURILoader class description
*/
void registerContentListener (in nsIURIContentListener aContentListener);
void unRegisterContentListener (in nsIURIContentListener aContentListener);
/**
* OpenURI requires the following parameters.....
* @param aChannel
* The channel that should be opened. This must not be asyncOpen'd yet!
* If a loadgroup is set on the channel, it will get replaced with a
* different one.
* @param aFlags
* Combination (bitwise OR) of the flags specified above. 0 indicates
* default handling.
* @param aWindowContext
* If you are running the url from a doc shell or a web shell, this is
* your window context. If you have a content listener you want to
* give first crack to, the uri loader needs to be able to get it
* from the window context. We will also be using the window context
* to get at the progress event sink interface.
* <b>Must not be null!</b>
*/
void openURI(in nsIChannel aChannel,
in unsigned long aFlags,
in nsIInterfaceRequestor aWindowContext);
/**
* Loads data from a channel. This differs from openURI in that the channel
* may already be opened, and that it returns a stream listener into which the
* caller should pump data. The caller is responsible for opening the channel
* and pumping the channel's data into the returned stream listener.
*
* Note: If the channel already has a loadgroup, it will be replaced with the
* window context's load group, or null if the context doesn't have one.
*
* If the window context's nsIURIContentListener refuses the load immediately
* (e.g. in nsIURIContentListener::onStartURIOpen), this method will return
* NS_ERROR_WONT_HANDLE_CONTENT. At that point, the caller should probably
* cancel the channel if it's already open (this method will not cancel the
* channel).
*
* If flags include DONT_RETARGET, and the content listener refuses the load
* during onStartRequest (e.g. in canHandleContent/isPreferred), then the
* returned stream listener's onStartRequest method will return
* NS_ERROR_WONT_HANDLE_CONTENT.
*
* @param aChannel
* The channel that should be loaded. The channel may already be
* opened. It must not be closed (i.e. this must be called before the
* channel calls onStopRequest on its stream listener).
* @param aFlags
* Combination (bitwise OR) of the flags specified above. 0 indicates
* default handling.
* @param aWindowContext
* If you are running the url from a doc shell or a web shell, this is
* your window context. If you have a content listener you want to
* give first crack to, the uri loader needs to be able to get it
* from the window context. We will also be using the window context
* to get at the progress event sink interface.
* <b>Must not be null!</b>
*/
nsIStreamListener openChannel(in nsIChannel aChannel,
in unsigned long aFlags,
in nsIInterfaceRequestor aWindowContext);
/**
* Stops an in progress load
*/
void stop(in nsISupports aLoadCookie);
};

View file

@ -0,0 +1,153 @@
/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
interface mozIDOMWindowProxy;
interface nsIWebProgressListener;
/**
* The nsIWebProgress interface is used to add or remove nsIWebProgressListener
* instances to observe the loading of asynchronous requests (usually in the
* context of a DOM window).
*
* nsIWebProgress instances may be arranged in a parent-child configuration,
* corresponding to the parent-child configuration of their respective DOM
* windows. However, in some cases a nsIWebProgress instance may not have an
* associated DOM window. The parent-child relationship of nsIWebProgress
* instances is not made explicit by this interface, but the relationship may
* exist in some implementations.
*
* A nsIWebProgressListener instance receives notifications for the
* nsIWebProgress instance to which it added itself, and it may also receive
* notifications from any nsIWebProgress instances that are children of that
* nsIWebProgress instance.
*/
[scriptable, uuid(c4d64640-b332-4db6-a2a5-e08566000dc9)]
interface nsIWebProgress : nsISupports
{
/**
* The following flags may be combined to form the aNotifyMask parameter for
* the addProgressListener method. They limit the set of events that are
* delivered to an nsIWebProgressListener instance.
*/
/**
* These flags indicate the state transistions to observe, corresponding to
* nsIWebProgressListener::onStateChange.
*
* NOTIFY_STATE_REQUEST
* Only receive the onStateChange event if the aStateFlags parameter
* includes nsIWebProgressListener::STATE_IS_REQUEST.
*
* NOTIFY_STATE_DOCUMENT
* Only receive the onStateChange event if the aStateFlags parameter
* includes nsIWebProgressListener::STATE_IS_DOCUMENT.
*
* NOTIFY_STATE_NETWORK
* Only receive the onStateChange event if the aStateFlags parameter
* includes nsIWebProgressListener::STATE_IS_NETWORK.
*
* NOTIFY_STATE_WINDOW
* Only receive the onStateChange event if the aStateFlags parameter
* includes nsIWebProgressListener::STATE_IS_WINDOW.
*
* NOTIFY_STATE_ALL
* Receive all onStateChange events.
*/
const unsigned long NOTIFY_STATE_REQUEST = 0x00000001;
const unsigned long NOTIFY_STATE_DOCUMENT = 0x00000002;
const unsigned long NOTIFY_STATE_NETWORK = 0x00000004;
const unsigned long NOTIFY_STATE_WINDOW = 0x00000008;
const unsigned long NOTIFY_STATE_ALL = 0x0000000f;
/**
* These flags indicate the other events to observe, corresponding to the
* other four methods defined on nsIWebProgressListener.
*
* NOTIFY_PROGRESS
* Receive onProgressChange events.
*
* NOTIFY_STATUS
* Receive onStatusChange events.
*
* NOTIFY_SECURITY
* Receive onSecurityChange events.
*
* NOTIFY_LOCATION
* Receive onLocationChange events.
*
* NOTIFY_REFRESH
* Receive onRefreshAttempted events.
* This is defined on nsIWebProgressListener2.
*/
const unsigned long NOTIFY_PROGRESS = 0x00000010;
const unsigned long NOTIFY_STATUS = 0x00000020;
const unsigned long NOTIFY_SECURITY = 0x00000040;
const unsigned long NOTIFY_LOCATION = 0x00000080;
const unsigned long NOTIFY_REFRESH = 0x00000100;
/**
* This flag enables all notifications.
*/
const unsigned long NOTIFY_ALL = 0x000001ff;
/**
* Registers a listener to receive web progress events.
*
* @param aListener
* The listener interface to be called when a progress event occurs.
* This object must also implement nsISupportsWeakReference.
* @param aNotifyMask
* The types of notifications to receive.
*
* @throw NS_ERROR_INVALID_ARG
* Indicates that aListener was either null or that it does not
* support weak references.
* @throw NS_ERROR_FAILURE
* Indicates that aListener was already registered.
*/
void addProgressListener(in nsIWebProgressListener aListener,
in unsigned long aNotifyMask);
/**
* Removes a previously registered listener of progress events.
*
* @param aListener
* The listener interface previously registered with a call to
* addProgressListener.
*
* @throw NS_ERROR_FAILURE
* Indicates that aListener was not registered.
*/
void removeProgressListener(in nsIWebProgressListener aListener);
/**
* The DOM window associated with this nsIWebProgress instance.
*
* @throw NS_ERROR_FAILURE
* Indicates that there is no associated DOM window.
*/
readonly attribute mozIDOMWindowProxy DOMWindow;
readonly attribute uint64_t DOMWindowID;
/**
* Indicates whether DOMWindow.top == DOMWindow.
*/
readonly attribute boolean isTopLevel;
/**
* Indicates whether or not a document is currently being loaded
* in the context of this nsIWebProgress instance.
*/
readonly attribute boolean isLoadingDocument;
/**
* Contains a load type as specified by the load* constants in
* nsIDocShellLoadInfo.idl.
*/
readonly attribute unsigned long loadType;
};

View file

@ -0,0 +1,425 @@
/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
interface nsIWebProgress;
interface nsIRequest;
interface nsIURI;
/**
* The nsIWebProgressListener interface is implemented by clients wishing to
* listen in on the progress associated with the loading of asynchronous
* requests in the context of a nsIWebProgress instance as well as any child
* nsIWebProgress instances. nsIWebProgress.idl describes the parent-child
* relationship of nsIWebProgress instances.
*/
[scriptable, uuid(a9df523b-efe2-421e-9d8e-3d7f807dda4c)]
interface nsIWebProgressListener : nsISupports
{
/**
* State Transition Flags
*
* These flags indicate the various states that requests may transition
* through as they are being loaded. These flags are mutually exclusive.
*
* For any given request, onStateChange is called once with the STATE_START
* flag, zero or more times with the STATE_TRANSFERRING flag or once with the
* STATE_REDIRECTING flag, and then finally once with the STATE_STOP flag.
* NOTE: For document requests, a second STATE_STOP is generated (see the
* description of STATE_IS_WINDOW for more details).
*
* STATE_START
* This flag indicates the start of a request. This flag is set when a
* request is initiated. The request is complete when onStateChange is
* called for the same request with the STATE_STOP flag set.
*
* STATE_REDIRECTING
* This flag indicates that a request is being redirected. The request
* passed to onStateChange is the request that is being redirected. When a
* redirect occurs, a new request is generated automatically to process the
* new request. Expect a corresponding STATE_START event for the new
* request, and a STATE_STOP for the redirected request.
*
* STATE_TRANSFERRING
* This flag indicates that data for a request is being transferred to an
* end consumer. This flag indicates that the request has been targeted,
* and that the user may start seeing content corresponding to the request.
*
* STATE_NEGOTIATING
* This flag is not used.
*
* STATE_STOP
* This flag indicates the completion of a request. The aStatus parameter
* to onStateChange indicates the final status of the request.
*/
const unsigned long STATE_START = 0x00000001;
const unsigned long STATE_REDIRECTING = 0x00000002;
const unsigned long STATE_TRANSFERRING = 0x00000004;
const unsigned long STATE_NEGOTIATING = 0x00000008;
const unsigned long STATE_STOP = 0x00000010;
/**
* State Type Flags
*
* These flags further describe the entity for which the state transition is
* occuring. These flags are NOT mutually exclusive (i.e., an onStateChange
* event may indicate some combination of these flags).
*
* STATE_IS_REQUEST
* This flag indicates that the state transition is for a request, which
* includes but is not limited to document requests. (See below for a
* description of document requests.) Other types of requests, such as
* requests for inline content (e.g., images and stylesheets) are
* considered normal requests.
*
* STATE_IS_DOCUMENT
* This flag indicates that the state transition is for a document request.
* This flag is set in addition to STATE_IS_REQUEST. A document request
* supports the nsIChannel interface and its loadFlags attribute includes
* the nsIChannel::LOAD_DOCUMENT_URI flag.
*
* A document request does not complete until all requests associated with
* the loading of its corresponding document have completed. This includes
* other document requests (e.g., corresponding to HTML <iframe> elements).
* The document corresponding to a document request is available via the
* DOMWindow attribute of onStateChange's aWebProgress parameter.
*
* STATE_IS_NETWORK
* This flag indicates that the state transition corresponds to the start
* or stop of activity in the indicated nsIWebProgress instance. This flag
* is accompanied by either STATE_START or STATE_STOP, and it may be
* combined with other State Type Flags.
*
* Unlike STATE_IS_WINDOW, this flag is only set when activity within the
* nsIWebProgress instance being observed starts or stops. If activity
* only occurs in a child nsIWebProgress instance, then this flag will be
* set to indicate the start and stop of that activity.
*
* For example, in the case of navigation within a single frame of a HTML
* frameset, a nsIWebProgressListener instance attached to the
* nsIWebProgress of the frameset window will receive onStateChange calls
* with the STATE_IS_NETWORK flag set to indicate the start and stop of
* said navigation. In other words, an observer of an outer window can
* determine when activity, that may be constrained to a child window or
* set of child windows, starts and stops.
*
* STATE_IS_WINDOW
* This flag indicates that the state transition corresponds to the start
* or stop of activity in the indicated nsIWebProgress instance. This flag
* is accompanied by either STATE_START or STATE_STOP, and it may be
* combined with other State Type Flags.
*
* This flag is similar to STATE_IS_DOCUMENT. However, when a document
* request completes, two onStateChange calls with STATE_STOP are
* generated. The document request is passed as aRequest to both calls.
* The first has STATE_IS_REQUEST and STATE_IS_DOCUMENT set, and the second
* has the STATE_IS_WINDOW flag set (and possibly the STATE_IS_NETWORK flag
* set as well -- see above for a description of when the STATE_IS_NETWORK
* flag may be set). This second STATE_STOP event may be useful as a way
* to partition the work that occurs when a document request completes.
*/
const unsigned long STATE_IS_REQUEST = 0x00010000;
const unsigned long STATE_IS_DOCUMENT = 0x00020000;
const unsigned long STATE_IS_NETWORK = 0x00040000;
const unsigned long STATE_IS_WINDOW = 0x00080000;
/**
* State Modifier Flags
*
* These flags further describe the transition which is occuring. These
* flags are NOT mutually exclusive (i.e., an onStateChange event may
* indicate some combination of these flags).
*
* STATE_RESTORING
* This flag indicates that the state transition corresponds to the start
* or stop of activity for restoring a previously-rendered presentation.
* As such, there is no actual network activity associated with this
* request, and any modifications made to the document or presentation
* when it was originally loaded will still be present.
*/
const unsigned long STATE_RESTORING = 0x01000000;
/**
* State Security Flags
*
* These flags describe the security state reported by a call to the
* onSecurityChange method. These flags are mutually exclusive.
*
* STATE_IS_INSECURE
* This flag indicates that the data corresponding to the request
* was received over an insecure channel.
*
* STATE_IS_BROKEN
* This flag indicates an unknown security state. This may mean that the
* request is being loaded as part of a page in which some content was
* received over an insecure channel.
*
* STATE_IS_SECURE
* This flag indicates that the data corresponding to the request was
* received over a secure channel. The degree of security is expressed by
* STATE_SECURE_HIGH, STATE_SECURE_MED, or STATE_SECURE_LOW.
*/
const unsigned long STATE_IS_INSECURE = 0x00000004;
const unsigned long STATE_IS_BROKEN = 0x00000001;
const unsigned long STATE_IS_SECURE = 0x00000002;
/**
* Mixed active content flags
*
* May be set in addition to the State Security Flags, to indicate that
* mixed active content has been encountered.
*
* STATE_BLOCKED_MIXED_ACTIVE_CONTENT
* Mixed active content has been blocked from loading.
*
* STATE_LOADED_MIXED_ACTIVE_CONTENT
* Mixed active content has been loaded. State should be STATE_IS_BROKEN.
*/
const unsigned long STATE_BLOCKED_MIXED_ACTIVE_CONTENT = 0x00000010;
const unsigned long STATE_LOADED_MIXED_ACTIVE_CONTENT = 0x00000020;
/**
* Mixed display content flags
*
* May be set in addition to the State Security Flags, to indicate that
* mixed display content has been encountered.
*
* STATE_BLOCKED_MIXED_DISPLAY_CONTENT
* Mixed display content has been blocked from loading.
*
* STATE_LOADED_MIXED_DISPLAY_CONTENT
* Mixed display content has been loaded. State should be STATE_IS_BROKEN.
*/
const unsigned long STATE_BLOCKED_MIXED_DISPLAY_CONTENT = 0x00000100;
const unsigned long STATE_LOADED_MIXED_DISPLAY_CONTENT = 0x00000200;
/**
* Tracking content flags
*
* May be set in addition to the State security Flags, to indicate that
* tracking content has been encountered.
*
* STATE_BLOCKED_TRACKING_CONTENT
* Tracking content has been blocked from loading.
*
* STATE_LOADED_TRACKING_CONTENT
* Tracking content has been loaded.
*/
const unsigned long STATE_BLOCKED_TRACKING_CONTENT = 0x00001000;
const unsigned long STATE_LOADED_TRACKING_CONTENT = 0x00002000;
/**
* Security Strength Flags
*
* These flags describe the security strength and accompany STATE_IS_SECURE
* in a call to the onSecurityChange method. These flags are mutually
* exclusive.
*
* These flags are not meant to provide a precise description of data
* transfer security. These are instead intended as a rough indicator that
* may be used to, for example, color code a security indicator or otherwise
* provide basic data transfer security feedback to the user.
*
* STATE_SECURE_HIGH
* This flag indicates a high degree of security.
*
* STATE_SECURE_MED
* This flag indicates a medium degree of security.
*
* STATE_SECURE_LOW
* This flag indicates a low degree of security.
*/
const unsigned long STATE_SECURE_HIGH = 0x00040000;
const unsigned long STATE_SECURE_MED = 0x00010000;
const unsigned long STATE_SECURE_LOW = 0x00020000;
/**
* State bits for EV == Extended Validation == High Assurance
*
* These flags describe the level of identity verification
* in a call to the onSecurityChange method.
*
* STATE_IDENTITY_EV_TOPLEVEL
* The topmost document uses an EV cert.
* NOTE: Available since Gecko 1.9
*/
const unsigned long STATE_IDENTITY_EV_TOPLEVEL = 0x00100000;
/**
* Broken state flags
*
* These flags describe the reason of the broken state.
*
* STATE_USES_SSL_3
* The topmost document uses SSL 3.0.
*
* STATE_USES_WEAK_CRYPTO
* The topmost document uses a weak cipher suite such as RC4.
*
* STATE_CERT_USER_OVERRIDDEN
* The user has added a security exception for the site.
*/
const unsigned long STATE_USES_SSL_3 = 0x01000000;
const unsigned long STATE_USES_WEAK_CRYPTO = 0x02000000;
const unsigned long STATE_CERT_USER_OVERRIDDEN = 0x04000000;
/**
* Notification indicating the state has changed for one of the requests
* associated with aWebProgress.
*
* @param aWebProgress
* The nsIWebProgress instance that fired the notification
* @param aRequest
* The nsIRequest that has changed state.
* @param aStateFlags
* Flags indicating the new state. This value is a combination of one
* of the State Transition Flags and one or more of the State Type
* Flags defined above. Any undefined bits are reserved for future
* use.
* @param aStatus
* Error status code associated with the state change. This parameter
* should be ignored unless aStateFlags includes the STATE_STOP bit.
* The status code indicates success or failure of the request
* associated with the state change. NOTE: aStatus may be a success
* code even for server generated errors, such as the HTTP 404 error.
* In such cases, the request itself should be queried for extended
* error information (e.g., for HTTP requests see nsIHttpChannel).
*/
void onStateChange(in nsIWebProgress aWebProgress,
in nsIRequest aRequest,
in unsigned long aStateFlags,
in nsresult aStatus);
/**
* Notification that the progress has changed for one of the requests
* associated with aWebProgress. Progress totals are reset to zero when all
* requests in aWebProgress complete (corresponding to onStateChange being
* called with aStateFlags including the STATE_STOP and STATE_IS_WINDOW
* flags).
*
* @param aWebProgress
* The nsIWebProgress instance that fired the notification.
* @param aRequest
* The nsIRequest that has new progress.
* @param aCurSelfProgress
* The current progress for aRequest.
* @param aMaxSelfProgress
* The maximum progress for aRequest.
* @param aCurTotalProgress
* The current progress for all requests associated with aWebProgress.
* @param aMaxTotalProgress
* The total progress for all requests associated with aWebProgress.
*
* NOTE: If any progress value is unknown, or if its value would exceed the
* maximum value of type long, then its value is replaced with -1.
*
* NOTE: If the object also implements nsIWebProgressListener2 and the caller
* knows about that interface, this function will not be called. Instead,
* nsIWebProgressListener2::onProgressChange64 will be called.
*/
void onProgressChange(in nsIWebProgress aWebProgress,
in nsIRequest aRequest,
in long aCurSelfProgress,
in long aMaxSelfProgress,
in long aCurTotalProgress,
in long aMaxTotalProgress);
/**
* Flags for onLocationChange
*
* LOCATION_CHANGE_SAME_DOCUMENT
* This flag is on when |aWebProgress| did not load a new document.
* For example, the location change is due to an anchor scroll or a
* pushState/popState/replaceState.
*
* LOCATION_CHANGE_ERROR_PAGE
* This flag is on when |aWebProgress| redirected from the requested
* contents to an internal page to show error status, such as
* <about:neterror>, <about:certerror> and so on.
*
* Generally speaking, |aURI| and |aRequest| are the original data. DOM
* |window.location.href| is also the original location, while
* |document.documentURI| is the redirected location. Sometimes |aURI| is
* <about:blank> and |aRequest| is null when the original data does not
+ remain.
*
* |aWebProgress| does NOT set this flag when it did not try to load a new
* document. In this case, it should set LOCATION_CHANGE_SAME_DOCUMENT.
*/
const unsigned long LOCATION_CHANGE_SAME_DOCUMENT = 0x00000001;
const unsigned long LOCATION_CHANGE_ERROR_PAGE = 0x00000002;
/**
* Called when the location of the window being watched changes. This is not
* when a load is requested, but rather once it is verified that the load is
* going to occur in the given window. For instance, a load that starts in a
* window might send progress and status messages for the new site, but it
* will not send the onLocationChange until we are sure that we are loading
* this new page here.
*
* @param aWebProgress
* The nsIWebProgress instance that fired the notification.
* @param aRequest
* The associated nsIRequest. This may be null in some cases.
* @param aLocation
* The URI of the location that is being loaded.
* @param aFlags
* This is a value which explains the situation or the reason why
* the location has changed.
*/
void onLocationChange(in nsIWebProgress aWebProgress,
in nsIRequest aRequest,
in nsIURI aLocation,
[optional] in unsigned long aFlags);
/**
* Notification that the status of a request has changed. The status message
* is intended to be displayed to the user (e.g., in the status bar of the
* browser).
*
* @param aWebProgress
* The nsIWebProgress instance that fired the notification.
* @param aRequest
* The nsIRequest that has new status.
* @param aStatus
* This value is not an error code. Instead, it is a numeric value
* that indicates the current status of the request. This interface
* does not define the set of possible status codes. NOTE: Some
* status values are defined by nsITransport and nsISocketTransport.
* @param aMessage
* Localized text corresponding to aStatus.
*/
void onStatusChange(in nsIWebProgress aWebProgress,
in nsIRequest aRequest,
in nsresult aStatus,
in wstring aMessage);
/**
* Notification called for security progress. This method will be called on
* security transitions (eg HTTP -> HTTPS, HTTPS -> HTTP, FOO -> HTTPS) and
* after document load completion. It might also be called if an error
* occurs during network loading.
*
* @param aWebProgress
* The nsIWebProgress instance that fired the notification.
* @param aRequest
* The nsIRequest that has new security state.
* @param aState
* A value composed of the Security State Flags and the Security
* Strength Flags listed above. Any undefined bits are reserved for
* future use.
*
* NOTE: These notifications will only occur if a security package is
* installed.
*/
void onSecurityChange(in nsIWebProgress aWebProgress,
in nsIRequest aRequest,
in unsigned long aState);
};

View file

@ -0,0 +1,69 @@
/* 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 "nsIWebProgressListener.idl"
/**
* An extended version of nsIWebProgressListener.
*/
[scriptable, uuid(dde39de0-e4e0-11da-8ad9-0800200c9a66)]
interface nsIWebProgressListener2 : nsIWebProgressListener {
/**
* Notification that the progress has changed for one of the requests
* associated with aWebProgress. Progress totals are reset to zero when all
* requests in aWebProgress complete (corresponding to onStateChange being
* called with aStateFlags including the STATE_STOP and STATE_IS_WINDOW
* flags).
*
* This function is identical to nsIWebProgressListener::onProgressChange,
* except that this function supports 64-bit values.
*
* @param aWebProgress
* The nsIWebProgress instance that fired the notification.
* @param aRequest
* The nsIRequest that has new progress.
* @param aCurSelfProgress
* The current progress for aRequest.
* @param aMaxSelfProgress
* The maximum progress for aRequest.
* @param aCurTotalProgress
* The current progress for all requests associated with aWebProgress.
* @param aMaxTotalProgress
* The total progress for all requests associated with aWebProgress.
*
* NOTE: If any progress value is unknown, then its value is replaced with -1.
*
* @see nsIWebProgressListener2::onProgressChange64
*/
void onProgressChange64(in nsIWebProgress aWebProgress,
in nsIRequest aRequest,
in long long aCurSelfProgress,
in long long aMaxSelfProgress,
in long long aCurTotalProgress,
in long long aMaxTotalProgress);
/**
* Notification that a refresh or redirect has been requested in aWebProgress
* For example, via a <meta http-equiv="refresh"> or an HTTP Refresh: header
*
* @param aWebProgress
* The nsIWebProgress instance that fired the notification.
* @param aRefreshURI
* The new URI that aWebProgress has requested redirecting to.
* @param aMillis
* The delay (in milliseconds) before refresh.
* @param aSameURI
* True if aWebProgress is requesting a refresh of the
* current URI.
* False if aWebProgress is requesting a redirection to
* a different URI.
*
* @return True if the refresh may proceed.
* False if the refresh should be aborted.
*/
boolean onRefreshAttempted(in nsIWebProgress aWebProgress,
in nsIURI aRefreshURI,
in long aMillis,
in boolean aSameURI);
};

View file

@ -0,0 +1,966 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sts=2 sw=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 "nsURILoader.h"
#include "nsAutoPtr.h"
#include "nsIURIContentListener.h"
#include "nsIContentHandler.h"
#include "nsILoadGroup.h"
#include "nsIDocumentLoader.h"
#include "nsIWebProgress.h"
#include "nsIWebProgressListener.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIStreamListener.h"
#include "nsIURI.h"
#include "nsIChannel.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIProgressEventSink.h"
#include "nsIInputStream.h"
#include "nsIStreamConverterService.h"
#include "nsWeakReference.h"
#include "nsIHttpChannel.h"
#include "nsIMultiPartChannel.h"
#include "netCore.h"
#include "nsCRT.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsXPIDLString.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsReadableUtils.h"
#include "nsError.h"
#include "nsICategoryManager.h"
#include "nsCExternalHandlerService.h" // contains contractids for the helper app service
#include "nsIMIMEHeaderParam.h"
#include "nsNetCID.h"
#include "nsMimeTypes.h"
#include "nsDocLoader.h"
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
#include "nsContentUtils.h"
mozilla::LazyLogModule nsURILoader::mLog("URILoader");
#define LOG(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Debug, args)
#define LOG_ERROR(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Error, args)
#define LOG_ENABLED() MOZ_LOG_TEST(nsURILoader::mLog, mozilla::LogLevel::Debug)
#define NS_PREF_DISABLE_BACKGROUND_HANDLING \
"security.exthelperapp.disable_background_handling"
/**
* The nsDocumentOpenInfo contains the state required when a single
* document is being opened in order to discover the content type...
* Each instance remains alive until its target URL has been loaded
* (or aborted).
*/
class nsDocumentOpenInfo final : public nsIStreamListener
, public nsIThreadRetargetableStreamListener
{
public:
// Needed for nsCOMPtr to work right... Don't call this!
nsDocumentOpenInfo();
// Real constructor
// aFlags is a combination of the flags on nsIURILoader
nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
uint32_t aFlags,
nsURILoader* aURILoader);
NS_DECL_THREADSAFE_ISUPPORTS
/**
* Prepares this object for receiving data. The stream
* listener methods of this class must not be called before calling this
* method.
*/
nsresult Prepare();
// Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
// take the data off our hands.
nsresult DispatchContent(nsIRequest *request, nsISupports * aCtxt);
// Call this if we need to insert a stream converter from aSrcContentType to
// aOutContentType into the StreamListener chain. DO NOT call it if the two
// types are the same, since no conversion is needed in that case.
nsresult ConvertData(nsIRequest *request,
nsIURIContentListener *aListener,
const nsACString & aSrcContentType,
const nsACString & aOutContentType);
/**
* Function to attempt to use aListener to handle the load. If
* true is returned, nothing else needs to be done; if false
* is returned, then a different way of handling the load should be
* tried.
*/
bool TryContentListener(nsIURIContentListener* aListener,
nsIChannel* aChannel);
// nsIRequestObserver methods:
NS_DECL_NSIREQUESTOBSERVER
// nsIStreamListener methods:
NS_DECL_NSISTREAMLISTENER
// nsIThreadRetargetableStreamListener
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
protected:
~nsDocumentOpenInfo();
protected:
/**
* The first content listener to try dispatching data to. Typically
* the listener associated with the entity that originated the load.
*/
nsCOMPtr<nsIURIContentListener> m_contentListener;
/**
* The stream listener to forward nsIStreamListener notifications
* to. This is set once the load is dispatched.
*/
nsCOMPtr<nsIStreamListener> m_targetStreamListener;
/**
* A pointer to the entity that originated the load. We depend on getting
* things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
*/
nsCOMPtr<nsIInterfaceRequestor> m_originalContext;
/**
* IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent
* (also determines whether we use CanHandleContent or IsPreferred).
* DONT_RETARGET means that we will only try m_originalContext, no other
* listeners.
*/
uint32_t mFlags;
/**
* The type of the data we will be trying to dispatch.
*/
nsCString mContentType;
/**
* Reference to the URILoader service so we can access its list of
* nsIURIContentListeners.
*/
RefPtr<nsURILoader> mURILoader;
};
NS_IMPL_ADDREF(nsDocumentOpenInfo)
NS_IMPL_RELEASE(nsDocumentOpenInfo)
NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
NS_INTERFACE_MAP_END_THREADSAFE
nsDocumentOpenInfo::nsDocumentOpenInfo()
{
NS_NOTREACHED("This should never be called\n");
}
nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
uint32_t aFlags,
nsURILoader* aURILoader)
: m_originalContext(aWindowContext),
mFlags(aFlags),
mURILoader(aURILoader)
{
}
nsDocumentOpenInfo::~nsDocumentOpenInfo()
{
}
nsresult nsDocumentOpenInfo::Prepare()
{
LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
nsresult rv;
// ask our window context if it has a uri content listener...
m_contentListener = do_GetInterface(m_originalContext, &rv);
return rv;
}
NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
{
LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
MOZ_ASSERT(request);
if (!request) {
return NS_ERROR_UNEXPECTED;
}
nsresult rv = NS_OK;
//
// Deal with "special" HTTP responses:
//
// - In the case of a 204 (No Content) or 205 (Reset Content) response, do
// not try to find a content handler. Return NS_BINDING_ABORTED to cancel
// the request. This has the effect of ensuring that the DocLoader does
// not try to interpret this as a real request.
//
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request, &rv));
if (NS_SUCCEEDED(rv)) {
uint32_t responseCode = 0;
rv = httpChannel->GetResponseStatus(&responseCode);
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to get HTTP response status"));
// behave as in the canceled case
return NS_OK;
}
LOG((" HTTP response status: %d", responseCode));
if (204 == responseCode || 205 == responseCode) {
return NS_BINDING_ABORTED;
}
static bool sLargeAllocationHeaderEnabled = false;
static bool sCachedLargeAllocationPref = false;
if (!sCachedLargeAllocationPref) {
sCachedLargeAllocationPref = true;
mozilla::Preferences::AddBoolVarCache(&sLargeAllocationHeaderEnabled,
"dom.largeAllocationHeader.enabled");
}
if (sLargeAllocationHeaderEnabled) {
// If we have a Large-Allocation header, let's check if we should perform a process switch.
nsAutoCString largeAllocationHeader;
rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Large-Allocation"), largeAllocationHeader);
if (NS_SUCCEEDED(rv) && nsContentUtils::AttemptLargeAllocationLoad(httpChannel)) {
return NS_BINDING_ABORTED;
}
}
}
//
// Make sure that the transaction has succeeded, so far...
//
nsresult status;
rv = request->GetStatus(&status);
NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get request status!");
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(status)) {
LOG_ERROR((" Request failed, status: 0x%08X", rv));
//
// The transaction has already reported an error - so it will be torn
// down. Therefore, it is not necessary to return an error code...
//
return NS_OK;
}
rv = DispatchContent(request, aCtxt);
LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08X", m_targetStreamListener.get(), rv));
NS_ASSERTION(NS_SUCCEEDED(rv) || !m_targetStreamListener,
"Must not have an m_targetStreamListener with a failure return!");
NS_ENSURE_SUCCESS(rv, rv);
if (m_targetStreamListener)
rv = m_targetStreamListener->OnStartRequest(request, aCtxt);
LOG((" OnStartRequest returning: 0x%08X", rv));
return rv;
}
NS_IMETHODIMP
nsDocumentOpenInfo::CheckListenerChain()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
nsresult rv = NS_OK;
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
do_QueryInterface(m_targetStreamListener, &rv);
if (retargetableListener) {
rv = retargetableListener->CheckListenerChain();
}
LOG(("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv %x",
this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
(nsIStreamListener*)m_targetStreamListener, rv));
return rv;
}
NS_IMETHODIMP
nsDocumentOpenInfo::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
nsIInputStream * inStr,
uint64_t sourceOffset, uint32_t count)
{
// if we have retarged to the end stream listener, then forward the call....
// otherwise, don't do anything
nsresult rv = NS_OK;
if (m_targetStreamListener)
rv = m_targetStreamListener->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count);
return rv;
}
NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest *request, nsISupports *aCtxt,
nsresult aStatus)
{
LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
if ( m_targetStreamListener)
{
nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
// If this is a multipart stream, we could get another
// OnStartRequest after this... reset state.
m_targetStreamListener = nullptr;
mContentType.Truncate();
listener->OnStopRequest(request, aCtxt, aStatus);
}
// Remember...
// In the case of multiplexed streams (such as multipart/x-mixed-replace)
// these stream listener methods could be called again :-)
//
return NS_OK;
}
nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports * aCtxt)
{
LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this, mContentType.get()));
NS_PRECONDITION(!m_targetStreamListener,
"Why do we already have a target stream listener?");
nsresult rv;
nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
if (!aChannel) {
LOG_ERROR((" Request is not a channel. Bailing."));
return NS_ERROR_FAILURE;
}
NS_NAMED_LITERAL_CSTRING(anyType, "*/*");
if (mContentType.IsEmpty() || mContentType == anyType) {
rv = aChannel->GetContentType(mContentType);
if (NS_FAILED(rv)) return rv;
LOG((" Got type from channel: '%s'", mContentType.get()));
}
bool isGuessFromExt =
mContentType.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT);
if (isGuessFromExt) {
// Reset to application/octet-stream for now; no one other than the
// external helper app service should see APPLICATION_GUESS_FROM_EXT.
mContentType = APPLICATION_OCTET_STREAM;
aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
}
// Check whether the data should be forced to be handled externally. This
// could happen because the Content-Disposition header is set so, or, in the
// future, because the user has specified external handling for the MIME
// type.
bool forceExternalHandling = false;
uint32_t disposition;
rv = aChannel->GetContentDisposition(&disposition);
bool allowContentDispositionToForceExternalHandling = true;
#ifdef MOZ_B2G
// On B2G, OMA content files should never be handled by an external handler
// (even if the server specifies Content-Disposition: attachment) because the
// data should never be stored on an unencrypted form.
allowContentDispositionToForceExternalHandling =
!mContentType.LowerCaseEqualsASCII("application/vnd.oma.drm.message");
#endif
if (NS_SUCCEEDED(rv) && (disposition == nsIChannel::DISPOSITION_ATTACHMENT) &&
allowContentDispositionToForceExternalHandling) {
forceExternalHandling = true;
}
LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
// The type or data the contentListener wants.
nsXPIDLCString desiredContentType;
if (!forceExternalHandling)
{
//
// First step: See whether m_contentListener wants to handle this
// content type.
//
if (m_contentListener && TryContentListener(m_contentListener, aChannel)) {
LOG((" Success! Our default listener likes this type"));
// All done here
return NS_OK;
}
// If we aren't allowed to try other listeners, just skip through to
// trying to convert the data.
if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
//
// Second step: See whether some other registered listener wants
// to handle this content type.
//
int32_t count = mURILoader->m_listeners.Count();
nsCOMPtr<nsIURIContentListener> listener;
for (int32_t i = 0; i < count; i++) {
listener = do_QueryReferent(mURILoader->m_listeners[i]);
if (listener) {
if (TryContentListener(listener, aChannel)) {
LOG((" Found listener registered on the URILoader"));
return NS_OK;
}
} else {
// remove from the listener list, reset i and update count
mURILoader->m_listeners.RemoveObjectAt(i--);
--count;
}
}
//
// Third step: Try to find a content listener that has not yet had
// the chance to register, as it is contained in a not-yet-loaded
// module, but which has registered a contract ID.
//
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (catman) {
nsXPIDLCString contractidString;
rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
mContentType.get(),
getter_Copies(contractidString));
if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
LOG((" Listener contractid for '%s' is '%s'",
mContentType.get(), contractidString.get()));
listener = do_CreateInstance(contractidString);
LOG((" Listener from category manager: 0x%p", listener.get()));
if (listener && TryContentListener(listener, aChannel)) {
LOG((" Listener from category manager likes this type"));
return NS_OK;
}
}
}
//
// Fourth step: try to find an nsIContentHandler for our type.
//
nsAutoCString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
handlerContractID += mContentType;
nsCOMPtr<nsIContentHandler> contentHandler =
do_CreateInstance(handlerContractID.get());
if (contentHandler) {
LOG((" Content handler found"));
rv = contentHandler->HandleContent(mContentType.get(),
m_originalContext, request);
// XXXbz returning an error code to represent handling the
// content is just bizarre!
if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
if (NS_FAILED(rv)) {
// The content handler has unexpectedly failed. Cancel the request
// just in case the handler didn't...
LOG((" Content handler failed. Aborting load"));
request->Cancel(rv);
}
else {
LOG((" Content handler taking over load"));
}
return rv;
}
}
} else {
LOG((" DONT_RETARGET flag set, so skipped over random other content "
"listeners and content handlers"));
}
//
// Fifth step: If no listener prefers this type, see if any stream
// converters exist to transform this content type into
// some other.
//
// Don't do this if the server sent us a MIME type of "*/*" because they saw
// it in our Accept header and got confused.
// XXXbz have to be careful here; may end up in some sort of bizarre infinite
// decoding loop.
if (mContentType != anyType) {
rv = ConvertData(request, m_contentListener, mContentType, anyType);
if (NS_FAILED(rv)) {
m_targetStreamListener = nullptr;
} else if (m_targetStreamListener) {
// We found a converter for this MIME type. We'll just pump data into it
// and let the downstream nsDocumentOpenInfo handle things.
LOG((" Converter taking over now"));
return NS_OK;
}
}
}
NS_ASSERTION(!m_targetStreamListener,
"If we found a listener, why are we not using it?");
if (mFlags & nsIURILoader::DONT_RETARGET) {
LOG((" External handling forced or (listener not interested and no "
"stream converter exists), and retargeting disallowed -> aborting"));
return NS_ERROR_WONT_HANDLE_CONTENT;
}
// Before dispatching to the external helper app service, check for an HTTP
// error page. If we got one, we don't want to handle it with a helper app,
// really.
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
if (httpChannel) {
bool requestSucceeded;
httpChannel->GetRequestSucceeded(&requestSucceeded);
if (!requestSucceeded) {
// returning error from OnStartRequest will cancel the channel
return NS_ERROR_FILE_NOT_FOUND;
}
}
// Sixth step:
//
// All attempts to dispatch this content have failed. Just pass it off to
// the helper app service.
//
//
// Optionally, we may want to disable background handling by the external
// helper application service.
//
if (mozilla::Preferences::GetBool(NS_PREF_DISABLE_BACKGROUND_HANDLING,
false)) {
// First, we will ensure that the parent docshell is in an active
// state as we will disallow all external application handling unless it is
// in the foreground.
nsCOMPtr<nsIDocShell> docShell(do_GetInterface(m_originalContext));
if (!docShell) {
// If we can't perform our security check we definitely don't want to go
// any further!
LOG(("Failed to get DocShell to ensure it is active before anding off to "
"helper app service. Aborting."));
return NS_ERROR_FAILURE;
}
// Ensure the DocShell is active before continuing.
bool isActive = false;
docShell->GetIsActive(&isActive);
if (!isActive) {
LOG((" Check for active DocShell returned false. Aborting hand off to "
"helper app service."));
return NS_ERROR_DOM_SECURITY_ERR;
}
}
nsCOMPtr<nsIExternalHelperAppService> helperAppService =
do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
if (helperAppService) {
LOG((" Passing load off to helper app service"));
// Set these flags to indicate that the channel has been targeted and that
// we are not using the original consumer.
nsLoadFlags loadFlags = 0;
request->GetLoadFlags(&loadFlags);
request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
| nsIChannel::LOAD_TARGETED);
if (isGuessFromExt) {
mContentType = APPLICATION_GUESS_FROM_EXT;
aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
}
rv = helperAppService->DoContent(mContentType,
request,
m_originalContext,
false,
nullptr,
getter_AddRefs(m_targetStreamListener));
if (NS_FAILED(rv)) {
request->SetLoadFlags(loadFlags);
m_targetStreamListener = nullptr;
}
}
NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
"There is no way we should be successful at this point without a m_targetStreamListener");
return rv;
}
nsresult
nsDocumentOpenInfo::ConvertData(nsIRequest *request,
nsIURIContentListener* aListener,
const nsACString& aSrcContentType,
const nsACString& aOutContentType)
{
LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
PromiseFlatCString(aSrcContentType).get(),
PromiseFlatCString(aOutContentType).get()));
NS_PRECONDITION(aSrcContentType != aOutContentType,
"ConvertData called when the two types are the same!");
nsresult rv = NS_OK;
nsCOMPtr<nsIStreamConverterService> StreamConvService =
do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
LOG((" Got converter service"));
// When applying stream decoders, it is necessary to "insert" an
// intermediate nsDocumentOpenInfo instance to handle the targeting of
// the "final" stream or streams.
//
// For certain content types (ie. multi-part/x-mixed-replace) the input
// stream is split up into multiple destination streams. This
// intermediate instance is used to target these "decoded" streams...
//
RefPtr<nsDocumentOpenInfo> nextLink =
new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
// Make sure nextLink starts with the contentListener that said it wanted the
// results of this decode.
nextLink->m_contentListener = aListener;
// Also make sure it has to look for a stream listener to pump data into.
nextLink->m_targetStreamListener = nullptr;
// Make sure that nextLink treats the data as aOutContentType when
// dispatching; that way even if the stream converters don't change the type
// on the channel we will still do the right thing. If aOutContentType is
// */*, that's OK -- that will just indicate to nextLink that it should get
// the type off the channel.
nextLink->mContentType = aOutContentType;
// The following call sets m_targetStreamListener to the input end of the
// stream converter and sets the output end of the stream converter to
// nextLink. As we pump data into m_targetStreamListener the stream
// converter will convert it and pass the converted data to nextLink.
return StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(),
PromiseFlatCString(aOutContentType).get(),
nextLink,
request,
getter_AddRefs(m_targetStreamListener));
}
bool
nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
nsIChannel* aChannel)
{
LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x",
this, mFlags));
NS_PRECONDITION(aListener, "Must have a non-null listener");
NS_PRECONDITION(aChannel, "Must have a channel");
bool listenerWantsContent = false;
nsXPIDLCString typeToUse;
if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) {
aListener->IsPreferred(mContentType.get(),
getter_Copies(typeToUse),
&listenerWantsContent);
} else {
aListener->CanHandleContent(mContentType.get(), false,
getter_Copies(typeToUse),
&listenerWantsContent);
}
if (!listenerWantsContent) {
LOG((" Listener is not interested"));
return false;
}
if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
// Need to do a conversion here.
nsresult rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
if (NS_FAILED(rv)) {
// No conversion path -- we don't want this listener, if we got one
m_targetStreamListener = nullptr;
}
LOG((" Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
// m_targetStreamListener is now the input end of the converter, and we can
// just pump the data in there, if it exists. If it does not, we need to
// try other nsIURIContentListeners.
return m_targetStreamListener != nullptr;
}
// At this point, aListener wants data of type mContentType. Let 'em have
// it. But first, if we are retargeting, set an appropriate flag on the
// channel
nsLoadFlags loadFlags = 0;
aChannel->GetLoadFlags(&loadFlags);
// Set this flag to indicate that the channel has been targeted at a final
// consumer. This load flag is tested in nsDocLoader::OnProgress.
nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED;
nsCOMPtr<nsIURIContentListener> originalListener =
do_GetInterface(m_originalContext);
if (originalListener != aListener) {
newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
}
aChannel->SetLoadFlags(loadFlags | newLoadFlags);
bool abort = false;
bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
nsresult rv = aListener->DoContent(mContentType,
isPreferred,
aChannel,
getter_AddRefs(m_targetStreamListener),
&abort);
if (NS_FAILED(rv)) {
LOG_ERROR((" DoContent failed"));
// Unset the RETARGETED_DOCUMENT_URI flag if we set it...
aChannel->SetLoadFlags(loadFlags);
m_targetStreamListener = nullptr;
return false;
}
if (abort) {
// Nothing else to do here -- aListener is handling it all. Make
// sure m_targetStreamListener is null so we don't do anything
// after this point.
LOG((" Listener has aborted the load"));
m_targetStreamListener = nullptr;
}
NS_ASSERTION(abort || m_targetStreamListener, "DoContent returned no listener?");
// aListener is handling the load from this point on.
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Implementation of nsURILoader
///////////////////////////////////////////////////////////////////////////////////////////////
nsURILoader::nsURILoader()
{
}
nsURILoader::~nsURILoader()
{
}
NS_IMPL_ADDREF(nsURILoader)
NS_IMPL_RELEASE(nsURILoader)
NS_INTERFACE_MAP_BEGIN(nsURILoader)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader)
NS_INTERFACE_MAP_ENTRY(nsIURILoader)
NS_INTERFACE_MAP_END
NS_IMETHODIMP nsURILoader::RegisterContentListener(nsIURIContentListener * aContentListener)
{
nsresult rv = NS_OK;
nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
NS_ASSERTION(weakListener, "your URIContentListener must support weak refs!\n");
if (weakListener)
m_listeners.AppendObject(weakListener);
return rv;
}
NS_IMETHODIMP nsURILoader::UnRegisterContentListener(nsIURIContentListener * aContentListener)
{
nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
if (weakListener)
m_listeners.RemoveObject(weakListener);
return NS_OK;
}
NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
uint32_t aFlags,
nsIInterfaceRequestor *aWindowContext)
{
NS_ENSURE_ARG_POINTER(channel);
if (LOG_ENABLED()) {
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
nsAutoCString spec;
uri->GetAsciiSpec(spec);
LOG(("nsURILoader::OpenURI for %s", spec.get()));
}
nsCOMPtr<nsIStreamListener> loader;
nsresult rv = OpenChannel(channel,
aFlags,
aWindowContext,
false,
getter_AddRefs(loader));
if (NS_SUCCEEDED(rv)) {
// this method is not complete!!! Eventually, we should first go
// to the content listener and ask them for a protocol handler...
// if they don't give us one, we need to go to the registry and get
// the preferred protocol handler.
// But for now, I'm going to let necko do the work for us....
rv = channel->AsyncOpen(loader, nullptr);
// no content from this load - that's OK.
if (rv == NS_ERROR_NO_CONTENT) {
LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
rv = NS_OK;
}
} else if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {
// Not really an error, from this method's point of view
rv = NS_OK;
}
return rv;
}
nsresult nsURILoader::OpenChannel(nsIChannel* channel,
uint32_t aFlags,
nsIInterfaceRequestor* aWindowContext,
bool aChannelIsOpen,
nsIStreamListener** aListener)
{
NS_ASSERTION(channel, "Trying to open a null channel!");
NS_ASSERTION(aWindowContext, "Window context must not be null");
if (LOG_ENABLED()) {
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
nsAutoCString spec;
uri->GetAsciiSpec(spec);
LOG(("nsURILoader::OpenChannel for %s", spec.get()));
}
// Let the window context's uriListener know that the open is starting. This
// gives that window a chance to abort the load process.
nsCOMPtr<nsIURIContentListener> winContextListener(do_GetInterface(aWindowContext));
if (winContextListener) {
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
if (uri) {
bool doAbort = false;
winContextListener->OnStartURIOpen(uri, &doAbort);
if (doAbort) {
LOG((" OnStartURIOpen aborted load"));
return NS_ERROR_WONT_HANDLE_CONTENT;
}
}
}
// we need to create a DocumentOpenInfo object which will go ahead and open
// the url and discover the content type....
RefPtr<nsDocumentOpenInfo> loader =
new nsDocumentOpenInfo(aWindowContext, aFlags, this);
// Set the correct loadgroup on the channel
nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext));
if (!loadGroup) {
// XXXbz This context is violating what we'd like to be the new uriloader
// api.... Set up a nsDocLoader to handle the loadgroup for this context.
// This really needs to go away!
nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext));
if (listener) {
nsCOMPtr<nsISupports> cookie;
listener->GetLoadCookie(getter_AddRefs(cookie));
if (!cookie) {
RefPtr<nsDocLoader> newDocLoader = new nsDocLoader();
nsresult rv = newDocLoader->Init();
if (NS_FAILED(rv))
return rv;
rv = nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader);
if (NS_FAILED(rv))
return rv;
cookie = nsDocLoader::GetAsSupports(newDocLoader);
listener->SetLoadCookie(cookie);
}
loadGroup = do_GetInterface(cookie);
}
}
// If the channel is pending, then we need to remove it from its current
// loadgroup
nsCOMPtr<nsILoadGroup> oldGroup;
channel->GetLoadGroup(getter_AddRefs(oldGroup));
if (aChannelIsOpen && !SameCOMIdentity(oldGroup, loadGroup)) {
// It is important to add the channel to the new group before
// removing it from the old one, so that the load isn't considered
// done as soon as the request is removed.
loadGroup->AddRequest(channel, nullptr);
if (oldGroup) {
oldGroup->RemoveRequest(channel, nullptr, NS_BINDING_RETARGETED);
}
}
channel->SetLoadGroup(loadGroup);
// prepare the loader for receiving data
nsresult rv = loader->Prepare();
if (NS_SUCCEEDED(rv))
NS_ADDREF(*aListener = loader);
return rv;
}
NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel,
uint32_t aFlags,
nsIInterfaceRequestor* aWindowContext,
nsIStreamListener** aListener)
{
bool pending;
if (NS_FAILED(channel->IsPending(&pending))) {
pending = false;
}
return OpenChannel(channel, aFlags, aWindowContext, pending, aListener);
}
NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie)
{
nsresult rv;
nsCOMPtr<nsIDocumentLoader> docLoader;
NS_ENSURE_ARG_POINTER(aLoadCookie);
docLoader = do_GetInterface(aLoadCookie, &rv);
if (docLoader) {
rv = docLoader->Stop();
}
return rv;
}

View file

@ -0,0 +1,59 @@
/* -*- 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 nsURILoader_h__
#define nsURILoader_h__
#include "nsCURILoader.h"
#include "nsISupportsUtils.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsString.h"
#include "nsIWeakReference.h"
#include "mozilla/Attributes.h"
#include "mozilla/Logging.h"
class nsDocumentOpenInfo;
class nsURILoader final : public nsIURILoader
{
public:
NS_DECL_NSIURILOADER
NS_DECL_ISUPPORTS
nsURILoader();
protected:
~nsURILoader();
/**
* Equivalent to nsIURILoader::openChannel, but allows specifying whether the
* channel is opened already.
*/
MOZ_MUST_USE nsresult OpenChannel(nsIChannel* channel,
uint32_t aFlags,
nsIInterfaceRequestor* aWindowContext,
bool aChannelOpen,
nsIStreamListener** aListener);
/**
* we shouldn't need to have an owning ref count on registered
* content listeners because they are supposed to unregister themselves
* when they go away. This array stores weak references
*/
nsCOMArray<nsIWeakReference> m_listeners;
/**
* Logging. The module is called "URILoader"
*/
static mozilla::LazyLogModule mLog;
friend class nsDocumentOpenInfo;
};
#endif /* nsURILoader_h__ */

View file

@ -0,0 +1,169 @@
#include "ContentHandlerService.h"
#include "HandlerServiceChild.h"
#include "ContentChild.h"
#include "nsIMutableArray.h"
#include "nsIMIMEInfo.h"
using mozilla::dom::ContentChild;
using mozilla::dom::PHandlerServiceChild;
using mozilla::dom::HandlerInfo;
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(ContentHandlerService, nsIHandlerService)
ContentHandlerService::ContentHandlerService()
{
}
nsresult
ContentHandlerService::Init()
{
if (!XRE_IsContentProcess()) {
return NS_ERROR_FAILURE;
}
ContentChild* cpc = ContentChild::GetSingleton();
mHandlerServiceChild = static_cast<HandlerServiceChild*>(cpc->SendPHandlerServiceConstructor());
return NS_OK;
}
void
ContentHandlerService::nsIHandlerInfoToHandlerInfo(nsIHandlerInfo* aInfo,
HandlerInfo* aHandlerInfo)
{
nsCString type;
aInfo->GetType(type);
nsCOMPtr<nsIMIMEInfo> mimeInfo = do_QueryInterface(aInfo);
bool isMIMEInfo = !!mimeInfo;
nsString description;
aInfo->GetDescription(description);
bool alwaysAskBeforeHandling;
aInfo->GetAlwaysAskBeforeHandling(&alwaysAskBeforeHandling);
nsCOMPtr<nsIHandlerApp> app;
aInfo->GetPreferredApplicationHandler(getter_AddRefs(app));
nsString name;
nsString detailedDescription;
if (app) {
app->GetName(name);
app->GetDetailedDescription(detailedDescription);
}
HandlerApp happ(name, detailedDescription);
nsTArray<HandlerApp> happs;
nsCOMPtr<nsIMutableArray> apps;
aInfo->GetPossibleApplicationHandlers(getter_AddRefs(apps));
if (apps) {
unsigned int length;
apps->GetLength(&length);
for (unsigned int i = 0; i < length; i++) {
apps->QueryElementAt(i, NS_GET_IID(nsIHandlerApp), getter_AddRefs(app));
app->GetName(name);
app->GetDetailedDescription(detailedDescription);
happs.AppendElement(HandlerApp(name, detailedDescription));
}
}
nsHandlerInfoAction action;
aInfo->GetPreferredAction(&action);
HandlerInfo info(type, isMIMEInfo, description, alwaysAskBeforeHandling, happ, happs, action);
*aHandlerInfo = info;
}
NS_IMETHODIMP RemoteHandlerApp::GetName(nsAString & aName)
{
aName.Assign(mAppChild.name());
return NS_OK;
}
NS_IMETHODIMP RemoteHandlerApp::SetName(const nsAString & aName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP RemoteHandlerApp::GetDetailedDescription(nsAString & aDetailedDescription)
{
aDetailedDescription.Assign(mAppChild.detailedDescription());
return NS_OK;
}
NS_IMETHODIMP RemoteHandlerApp::SetDetailedDescription(const nsAString & aDetailedDescription)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP RemoteHandlerApp::Equals(nsIHandlerApp *aHandlerApp, bool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP RemoteHandlerApp::LaunchWithURI(nsIURI *aURI, nsIInterfaceRequestor *aWindowContext)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMPL_ISUPPORTS(RemoteHandlerApp, nsIHandlerApp)
static inline void CopyHanderInfoTonsIHandlerInfo(HandlerInfo info, nsIHandlerInfo* aHandlerInfo)
{
HandlerApp preferredApplicationHandler = info.preferredApplicationHandler();
nsCOMPtr<nsIHandlerApp> preferredApp(new RemoteHandlerApp(preferredApplicationHandler));
aHandlerInfo->SetPreferredApplicationHandler(preferredApp);
nsCOMPtr<nsIMutableArray> possibleHandlers;
aHandlerInfo->GetPossibleApplicationHandlers(getter_AddRefs(possibleHandlers));
possibleHandlers->AppendElement(preferredApp, false);
}
ContentHandlerService::~ContentHandlerService()
{
}
NS_IMETHODIMP ContentHandlerService::Enumerate(nsISimpleEnumerator * *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP ContentHandlerService::FillHandlerInfo(nsIHandlerInfo *aHandlerInfo, const nsACString & aOverrideType)
{
HandlerInfo info;
nsIHandlerInfoToHandlerInfo(aHandlerInfo, &info);
mHandlerServiceChild->SendFillHandlerInfo(info, nsCString(aOverrideType), &info);
CopyHanderInfoTonsIHandlerInfo(info, aHandlerInfo);
return NS_OK;
}
NS_IMETHODIMP ContentHandlerService::Store(nsIHandlerInfo *aHandlerInfo)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP ContentHandlerService::Exists(nsIHandlerInfo *aHandlerInfo, bool *_retval)
{
HandlerInfo info;
nsIHandlerInfoToHandlerInfo(aHandlerInfo, &info);
mHandlerServiceChild->SendExists(info, _retval);
return NS_OK;
}
NS_IMETHODIMP ContentHandlerService::Remove(nsIHandlerInfo *aHandlerInfo)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP ContentHandlerService::GetTypeFromExtension(const nsACString & aFileExtension, nsACString & _retval)
{
nsCString* cachedType = nullptr;
if (!!mExtToTypeMap.Get(aFileExtension, &cachedType) && !!cachedType) {
_retval.Assign(*cachedType);
return NS_OK;
}
nsCString type;
mHandlerServiceChild->SendGetTypeFromExtension(nsCString(aFileExtension), &type);
_retval.Assign(type);
mExtToTypeMap.Put(nsCString(aFileExtension), new nsCString(type));
return NS_OK;
}
}
}

View file

@ -0,0 +1,52 @@
#ifndef ContentHandlerService_h
#define ContentHandlerService_h
#include "nsIHandlerService.h"
#include "nsClassHashtable.h"
#include "HandlerServiceChild.h"
#include "nsIMIMEInfo.h"
#define NS_CONTENTHANDLERSERVICE_CID \
{0xc4b6fb7c, 0xbfb1, 0x49dc, {0xa6, 0x5f, 0x03, 0x57, 0x96, 0x52, 0x4b, 0x53}}
namespace mozilla {
namespace dom {
class PHandlerServiceChild;
class ContentHandlerService : public nsIHandlerService
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERSERVICE
ContentHandlerService();
MOZ_MUST_USE nsresult Init();
static void nsIHandlerInfoToHandlerInfo(nsIHandlerInfo* aInfo, HandlerInfo* aHandlerInfo);
private:
virtual ~ContentHandlerService();
RefPtr<HandlerServiceChild> mHandlerServiceChild;
nsClassHashtable<nsCStringHashKey, nsCString> mExtToTypeMap;
};
class RemoteHandlerApp : public nsIHandlerApp
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
explicit RemoteHandlerApp(HandlerApp aAppChild) : mAppChild(aAppChild)
{
}
private:
virtual ~RemoteHandlerApp()
{
}
HandlerApp mAppChild;
};
}
}
#endif

View file

@ -0,0 +1,127 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "ExternalHelperAppChild.h"
#include "mozilla/net/ChannelDiverterChild.h"
#include "nsIDivertableChannel.h"
#include "nsIInputStream.h"
#include "nsIFTPChannel.h"
#include "nsIRequest.h"
#include "nsIResumableChannel.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(ExternalHelperAppChild,
nsIStreamListener,
nsIRequestObserver)
ExternalHelperAppChild::ExternalHelperAppChild()
: mStatus(NS_OK)
{
}
ExternalHelperAppChild::~ExternalHelperAppChild()
{
}
//-----------------------------------------------------------------------------
// nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
ExternalHelperAppChild::OnDataAvailable(nsIRequest *request,
nsISupports *ctx,
nsIInputStream *input,
uint64_t offset,
uint32_t count)
{
if (NS_FAILED(mStatus))
return mStatus;
nsCString data;
nsresult rv = NS_ReadInputStreamToString(input, data, count);
if (NS_FAILED(rv))
return rv;
if (!SendOnDataAvailable(data, offset, count))
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
//////////////////////////////////////////////////////////////////////////////
// nsIRequestObserver
//////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
ExternalHelperAppChild::OnStartRequest(nsIRequest *request, nsISupports *ctx)
{
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
if (divertable) {
return DivertToParent(divertable, request);
}
nsresult rv = mHandler->OnStartRequest(request, ctx);
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
nsCString entityID;
nsCOMPtr<nsIResumableChannel> resumable(do_QueryInterface(request));
if (resumable) {
resumable->GetEntityID(entityID);
}
SendOnStartRequest(entityID);
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppChild::OnStopRequest(nsIRequest *request,
nsISupports *ctx,
nsresult status)
{
// mHandler can be null if we diverted the request to the parent
if (mHandler) {
nsresult rv = mHandler->OnStopRequest(request, ctx, status);
SendOnStopRequest(status);
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
}
return NS_OK;
}
nsresult
ExternalHelperAppChild::DivertToParent(nsIDivertableChannel *divertable,
nsIRequest *request)
{
// nsIDivertable must know about content conversions before being diverted.
MOZ_ASSERT(mHandler);
mHandler->MaybeApplyDecodingForExtension(request);
mozilla::net::ChannelDiverterChild *diverter = nullptr;
nsresult rv = divertable->DivertToParent(&diverter);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(diverter);
if (SendDivertToParentUsing(diverter)) {
mHandler->DidDivertRequest(request);
mHandler = nullptr;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
bool
ExternalHelperAppChild::RecvCancel(const nsresult& aStatus)
{
mStatus = aStatus;
return true;
}
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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/. */
#ifndef mozilla_dom_ExternalHelperAppChild_h
#define mozilla_dom_ExternalHelperAppChild_h
#include "mozilla/dom/PExternalHelperAppChild.h"
#include "nsExternalHelperAppService.h"
#include "nsIStreamListener.h"
class nsIDivertableChannel;
namespace mozilla {
namespace dom {
class ExternalHelperAppChild : public PExternalHelperAppChild
, public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
ExternalHelperAppChild();
// Give the listener a real nsExternalAppHandler to complete processing on
// the child.
void SetHandler(nsExternalAppHandler *handler) { mHandler = handler; }
virtual bool RecvCancel(const nsresult& aStatus) override;
private:
virtual ~ExternalHelperAppChild();
MOZ_MUST_USE nsresult DivertToParent(nsIDivertableChannel *divertable, nsIRequest *request);
RefPtr<nsExternalAppHandler> mHandler;
nsresult mStatus;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ExternalHelperAppChild_h

View file

@ -0,0 +1,512 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "mozilla/DebugOnly.h"
#include "ExternalHelperAppParent.h"
#include "nsIContent.h"
#include "nsCExternalHandlerService.h"
#include "nsIExternalHelperAppService.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabParent.h"
#include "nsIBrowserDOMWindow.h"
#include "nsStringStream.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsNetUtil.h"
#include "nsIDocument.h"
#include "mozilla/net/ChannelDiverterParent.h"
#include "mozilla/Unused.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS_INHERITED(ExternalHelperAppParent,
nsHashPropertyBag,
nsIRequest,
nsIChannel,
nsIMultiPartChannel,
nsIPrivateBrowsingChannel,
nsIResumableChannel,
nsIStreamListener,
nsIExternalHelperAppParent)
ExternalHelperAppParent::ExternalHelperAppParent(
const OptionalURIParams& uri,
const int64_t& aContentLength,
const bool& aWasFileChannel)
: mURI(DeserializeURI(uri))
, mPending(false)
#ifdef DEBUG
, mDiverted(false)
#endif
, mIPCClosed(false)
, mLoadFlags(0)
, mStatus(NS_OK)
, mContentLength(aContentLength)
, mWasFileChannel(aWasFileChannel)
{
}
void
ExternalHelperAppParent::Init(ContentParent *parent,
const nsCString& aMimeContentType,
const nsCString& aContentDispositionHeader,
const uint32_t& aContentDispositionHint,
const nsString& aContentDispositionFilename,
const bool& aForceSave,
const OptionalURIParams& aReferrer,
PBrowserParent* aBrowser)
{
nsCOMPtr<nsIExternalHelperAppService> helperAppService =
do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID);
NS_ASSERTION(helperAppService, "No Helper App Service!");
nsCOMPtr<nsIURI> referrer = DeserializeURI(aReferrer);
if (referrer)
SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"), referrer);
mContentDispositionHeader = aContentDispositionHeader;
if (!mContentDispositionHeader.IsEmpty()) {
NS_GetFilenameFromDisposition(mContentDispositionFilename,
mContentDispositionHeader,
mURI);
mContentDisposition =
NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
}
else {
mContentDisposition = aContentDispositionHint;
mContentDispositionFilename = aContentDispositionFilename;
}
nsCOMPtr<nsIInterfaceRequestor> window;
if (aBrowser) {
TabParent* tabParent = TabParent::GetFrom(aBrowser);
if (tabParent->GetOwnerElement())
window = do_QueryInterface(tabParent->GetOwnerElement()->OwnerDoc()->GetWindow());
bool isPrivate = false;
nsCOMPtr<nsILoadContext> loadContext = tabParent->GetLoadContext();
loadContext->GetUsePrivateBrowsing(&isPrivate);
SetPrivate(isPrivate);
}
helperAppService->DoContent(aMimeContentType, this, window,
aForceSave, nullptr,
getter_AddRefs(mListener));
}
void
ExternalHelperAppParent::ActorDestroy(ActorDestroyReason why)
{
mIPCClosed = true;
}
void
ExternalHelperAppParent::Delete()
{
if (!mIPCClosed) {
Unused << Send__delete__(this);
}
}
bool
ExternalHelperAppParent::RecvOnStartRequest(const nsCString& entityID)
{
MOZ_ASSERT(!mDiverted, "child forwarding callbacks after request was diverted");
mEntityID = entityID;
mPending = true;
mStatus = mListener->OnStartRequest(this, nullptr);
return true;
}
bool
ExternalHelperAppParent::RecvOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
{
if (NS_FAILED(mStatus))
return true;
MOZ_ASSERT(!mDiverted, "child forwarding callbacks after request was diverted");
MOZ_ASSERT(mPending, "must be pending!");
nsCOMPtr<nsIInputStream> stringStream;
DebugOnly<nsresult> rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(), count, NS_ASSIGNMENT_DEPEND);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create dependent string!");
mStatus = mListener->OnDataAvailable(this, nullptr, stringStream, offset, count);
return true;
}
bool
ExternalHelperAppParent::RecvOnStopRequest(const nsresult& code)
{
MOZ_ASSERT(!mDiverted, "child forwarding callbacks after request was diverted");
mPending = false;
mListener->OnStopRequest(this, nullptr,
(NS_SUCCEEDED(code) && NS_FAILED(mStatus)) ? mStatus : code);
Delete();
return true;
}
bool
ExternalHelperAppParent::RecvDivertToParentUsing(PChannelDiverterParent* diverter)
{
MOZ_ASSERT(diverter);
auto p = static_cast<mozilla::net::ChannelDiverterParent*>(diverter);
p->DivertTo(this);
#ifdef DEBUG
mDiverted = true;
#endif
Unused << p->Send__delete__(p);
return true;
}
//
// nsIStreamListener
//
NS_IMETHODIMP
ExternalHelperAppParent::OnDataAvailable(nsIRequest *request,
nsISupports *ctx,
nsIInputStream *input,
uint64_t offset,
uint32_t count)
{
MOZ_ASSERT(mDiverted);
return mListener->OnDataAvailable(request, ctx, input, offset, count);
}
NS_IMETHODIMP
ExternalHelperAppParent::OnStartRequest(nsIRequest *request, nsISupports *ctx)
{
MOZ_ASSERT(mDiverted);
return mListener->OnStartRequest(request, ctx);
}
NS_IMETHODIMP
ExternalHelperAppParent::OnStopRequest(nsIRequest *request,
nsISupports *ctx,
nsresult status)
{
MOZ_ASSERT(mDiverted);
nsresult rv = mListener->OnStopRequest(request, ctx, status);
Delete();
return rv;
}
ExternalHelperAppParent::~ExternalHelperAppParent()
{
}
//
// nsIRequest implementation...
//
NS_IMETHODIMP
ExternalHelperAppParent::GetName(nsACString& aResult)
{
if (!mURI) {
aResult.Truncate();
return NS_ERROR_NOT_AVAILABLE;
}
mURI->GetAsciiSpec(aResult);
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::IsPending(bool *aResult)
{
*aResult = mPending;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetStatus(nsresult *aResult)
{
*aResult = mStatus;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::Cancel(nsresult aStatus)
{
mStatus = aStatus;
Unused << SendCancel(aStatus);
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::Suspend()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::Resume()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
//
// nsIChannel implementation
//
NS_IMETHODIMP
ExternalHelperAppParent::GetOriginalURI(nsIURI * *aURI)
{
NS_IF_ADDREF(*aURI = mURI);
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetOriginalURI(nsIURI *aURI)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetURI(nsIURI **aURI)
{
NS_IF_ADDREF(*aURI = mURI);
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::Open(nsIInputStream **aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::Open2(nsIInputStream** aStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::AsyncOpen(nsIStreamListener *aListener,
nsISupports *aContext)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::AsyncOpen2(nsIStreamListener *aListener)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetLoadFlags(nsLoadFlags *aLoadFlags)
{
*aLoadFlags = mLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetLoadFlags(nsLoadFlags aLoadFlags)
{
mLoadFlags = aLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = nullptr;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetOwner(nsISupports* *aOwner)
{
*aOwner = nullptr;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetOwner(nsISupports* aOwner)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetLoadInfo(nsILoadInfo* *aLoadInfo)
{
*aLoadInfo = nullptr;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetLoadInfo(nsILoadInfo* aLoadInfo)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
{
*aCallbacks = nullptr;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nullptr;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetContentType(nsACString& aContentType)
{
aContentType.Truncate();
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetContentType(const nsACString& aContentType)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetContentCharset(nsACString& aContentCharset)
{
aContentCharset.Truncate();
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetContentCharset(const nsACString& aContentCharset)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetContentDisposition(uint32_t *aContentDisposition)
{
// NB: mContentDisposition may or may not be set to a non UINT32_MAX value in
// nsExternalHelperAppService::DoContentContentProcessHelper
if (mContentDispositionHeader.IsEmpty() && mContentDisposition == UINT32_MAX)
return NS_ERROR_NOT_AVAILABLE;
*aContentDisposition = mContentDisposition;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetContentDisposition(uint32_t aContentDisposition)
{
mContentDisposition = aContentDisposition;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetContentDispositionFilename(nsAString& aContentDispositionFilename)
{
if (mContentDispositionFilename.IsEmpty())
return NS_ERROR_NOT_AVAILABLE;
aContentDispositionFilename = mContentDispositionFilename;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
{
mContentDispositionFilename = aContentDispositionFilename;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetContentDispositionHeader(nsACString& aContentDispositionHeader)
{
if (mContentDispositionHeader.IsEmpty())
return NS_ERROR_NOT_AVAILABLE;
aContentDispositionHeader = mContentDispositionHeader;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetContentLength(int64_t *aContentLength)
{
if (mContentLength < 0)
*aContentLength = -1;
else
*aContentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP
ExternalHelperAppParent::SetContentLength(int64_t aContentLength)
{
mContentLength = aContentLength;
return NS_OK;
}
//
// nsIResumableChannel implementation
//
NS_IMETHODIMP
ExternalHelperAppParent::ResumeAt(uint64_t startPos, const nsACString& entityID)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetEntityID(nsACString& aEntityID)
{
aEntityID = mEntityID;
return NS_OK;
}
//
// nsIMultiPartChannel implementation
//
NS_IMETHODIMP
ExternalHelperAppParent::GetBaseChannel(nsIChannel* *aChannel)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetPartID(uint32_t* aPartID)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ExternalHelperAppParent::GetIsLastPart(bool* aIsLastPart)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "mozilla/dom/PExternalHelperAppParent.h"
#include "nsIChannel.h"
#include "nsIMultiPartChannel.h"
#include "nsIResumableChannel.h"
#include "nsIStreamListener.h"
#include "nsHashPropertyBag.h"
#include "PrivateBrowsingChannel.h"
namespace IPC {
class URI;
} // namespace IPC
namespace mozilla {
namespace ipc {
class OptionalURIParams;
} // namespace ipc
namespace net {
class PChannelDiverterParent;
} // namespace net
namespace dom {
#define NS_IEXTERNALHELPERAPPPARENT_IID \
{ 0x127a01bc, 0x2a49, 0x46a8, \
{ 0x8c, 0x63, 0x4b, 0x5d, 0x3c, 0xa4, 0x07, 0x9c } }
class nsIExternalHelperAppParent : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IEXTERNALHELPERAPPPARENT_IID)
/**
* Returns true if this fake channel represented a file channel in the child.
*/
virtual bool WasFileChannel() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIExternalHelperAppParent, NS_IEXTERNALHELPERAPPPARENT_IID)
class ContentParent;
class PBrowserParent;
class ExternalHelperAppParent : public PExternalHelperAppParent
, public nsHashPropertyBag
, public nsIChannel
, public nsIMultiPartChannel
, public nsIResumableChannel
, public nsIStreamListener
, public net::PrivateBrowsingChannel<ExternalHelperAppParent>
, public nsIExternalHelperAppParent
{
typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIREQUEST
NS_DECL_NSICHANNEL
NS_DECL_NSIMULTIPARTCHANNEL
NS_DECL_NSIRESUMABLECHANNEL
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
bool RecvOnStartRequest(const nsCString& entityID) override;
bool RecvOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count) override;
bool RecvOnStopRequest(const nsresult& code) override;
bool RecvDivertToParentUsing(PChannelDiverterParent* diverter) override;
bool WasFileChannel() override {
return mWasFileChannel;
}
ExternalHelperAppParent(const OptionalURIParams& uri, const int64_t& contentLength,
const bool& wasFileChannel);
void Init(ContentParent *parent,
const nsCString& aMimeContentType,
const nsCString& aContentDisposition,
const uint32_t& aContentDispositionHint,
const nsString& aContentDispositionFilename,
const bool& aForceSave,
const OptionalURIParams& aReferrer,
PBrowserParent* aBrowser);
protected:
virtual ~ExternalHelperAppParent();
virtual void ActorDestroy(ActorDestroyReason why) override;
void Delete();
private:
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsIURI> mURI;
bool mPending;
#ifdef DEBUG
bool mDiverted;
#endif
bool mIPCClosed;
nsLoadFlags mLoadFlags;
nsresult mStatus;
int64_t mContentLength;
bool mWasFileChannel;
uint32_t mContentDisposition;
nsString mContentDispositionFilename;
nsCString mContentDispositionHeader;
nsCString mEntityID;
};
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,15 @@
#ifndef handler_service_child_h
#define handler_service_child_h
#include "mozilla/dom/PHandlerServiceChild.h"
class HandlerServiceChild final : public mozilla::dom::PHandlerServiceChild
{
public:
NS_INLINE_DECL_REFCOUNTING(HandlerServiceChild)
HandlerServiceChild() {}
private:
virtual ~HandlerServiceChild() {}
};
#endif

View file

@ -0,0 +1,270 @@
#include "HandlerServiceParent.h"
#include "nsIHandlerService.h"
#include "nsIMIMEInfo.h"
#include "ContentHandlerService.h"
using mozilla::dom::HandlerInfo;
using mozilla::dom::HandlerApp;
using mozilla::dom::ContentHandlerService;
using mozilla::dom::RemoteHandlerApp;
namespace {
class ProxyHandlerInfo final : public nsIHandlerInfo {
public:
explicit ProxyHandlerInfo(const HandlerInfo& aHandlerInfo);
NS_DECL_ISUPPORTS;
NS_DECL_NSIHANDLERINFO;
protected:
~ProxyHandlerInfo() {}
HandlerInfo mHandlerInfo;
nsHandlerInfoAction mPrefAction;
nsCOMPtr<nsIMutableArray> mPossibleApps;
};
NS_IMPL_ISUPPORTS(ProxyHandlerInfo, nsIHandlerInfo)
ProxyHandlerInfo::ProxyHandlerInfo(const HandlerInfo& aHandlerInfo) : mHandlerInfo(aHandlerInfo), mPossibleApps(do_CreateInstance(NS_ARRAY_CONTRACTID))
{
for (auto& happ : aHandlerInfo.possibleApplicationHandlers()) {
mPossibleApps->AppendElement(new RemoteHandlerApp(happ), false);
}
}
/* readonly attribute ACString type; */
NS_IMETHODIMP ProxyHandlerInfo::GetType(nsACString & aType)
{
aType.Assign(mHandlerInfo.type());
return NS_OK;
}
/* attribute AString description; */
NS_IMETHODIMP ProxyHandlerInfo::GetDescription(nsAString & aDescription)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP ProxyHandlerInfo::SetDescription(const nsAString & aDescription)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* attribute nsIHandlerApp preferredApplicationHandler; */
NS_IMETHODIMP ProxyHandlerInfo::GetPreferredApplicationHandler(nsIHandlerApp * *aPreferredApplicationHandler)
{
*aPreferredApplicationHandler = new RemoteHandlerApp(mHandlerInfo.preferredApplicationHandler());
NS_IF_ADDREF(*aPreferredApplicationHandler);
return NS_OK;
}
NS_IMETHODIMP ProxyHandlerInfo::SetPreferredApplicationHandler(nsIHandlerApp *aApp)
{
nsString name;
nsString detailedDescription;
if (aApp) {
aApp->GetName(name);
aApp->GetDetailedDescription(detailedDescription);
}
HandlerApp happ(name, detailedDescription);
mHandlerInfo = HandlerInfo(mHandlerInfo.type(),
mHandlerInfo.isMIMEInfo(),
mHandlerInfo.description(),
mHandlerInfo.alwaysAskBeforeHandling(),
happ,
mHandlerInfo.possibleApplicationHandlers(),
mHandlerInfo.preferredAction());
return NS_OK;
}
/* readonly attribute nsIMutableArray possibleApplicationHandlers; */
NS_IMETHODIMP ProxyHandlerInfo::GetPossibleApplicationHandlers(nsIMutableArray * *aPossibleApplicationHandlers)
{
*aPossibleApplicationHandlers = mPossibleApps;
NS_IF_ADDREF(*aPossibleApplicationHandlers);
return NS_OK;
}
/* readonly attribute boolean hasDefaultHandler; */
NS_IMETHODIMP ProxyHandlerInfo::GetHasDefaultHandler(bool *aHasDefaultHandler)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute AString defaultDescription; */
NS_IMETHODIMP ProxyHandlerInfo::GetDefaultDescription(nsAString & aDefaultDescription)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void launchWithURI (in nsIURI aURI, [optional] in nsIInterfaceRequestor aWindowContext); */
NS_IMETHODIMP ProxyHandlerInfo::LaunchWithURI(nsIURI *aURI, nsIInterfaceRequestor *aWindowContext)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* attribute ProxyHandlerInfoAction preferredAction; */
NS_IMETHODIMP ProxyHandlerInfo::GetPreferredAction(nsHandlerInfoAction *aPreferredAction)
{
*aPreferredAction = mPrefAction;
return NS_OK;
}
NS_IMETHODIMP ProxyHandlerInfo::SetPreferredAction(nsHandlerInfoAction aPreferredAction)
{
mHandlerInfo = HandlerInfo(mHandlerInfo.type(),
mHandlerInfo.isMIMEInfo(),
mHandlerInfo.description(),
mHandlerInfo.alwaysAskBeforeHandling(),
mHandlerInfo.preferredApplicationHandler(),
mHandlerInfo.possibleApplicationHandlers(),
aPreferredAction);
mPrefAction = aPreferredAction;
return NS_OK;
}
/* attribute boolean alwaysAskBeforeHandling; */
NS_IMETHODIMP ProxyHandlerInfo::GetAlwaysAskBeforeHandling(bool *aAlwaysAskBeforeHandling)
{
*aAlwaysAskBeforeHandling = mHandlerInfo.alwaysAskBeforeHandling();
return NS_OK;
}
NS_IMETHODIMP ProxyHandlerInfo::SetAlwaysAskBeforeHandling(bool aAlwaysAskBeforeHandling)
{
mHandlerInfo = HandlerInfo(mHandlerInfo.type(),
mHandlerInfo.isMIMEInfo(),
mHandlerInfo.description(),
aAlwaysAskBeforeHandling,
mHandlerInfo.preferredApplicationHandler(),
mHandlerInfo.possibleApplicationHandlers(),
mHandlerInfo.preferredAction());
return NS_OK;
}
class ProxyMIMEInfo : public nsIMIMEInfo
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMIMEINFO
NS_FORWARD_NSIHANDLERINFO(mProxyHandlerInfo->);
explicit ProxyMIMEInfo(HandlerInfo aHandlerInfo) : mProxyHandlerInfo(new ProxyHandlerInfo(aHandlerInfo)) {}
private:
virtual ~ProxyMIMEInfo() {}
nsCOMPtr<nsIHandlerInfo> mProxyHandlerInfo;
protected:
/* additional members */
};
NS_IMPL_ISUPPORTS(ProxyMIMEInfo, nsIMIMEInfo, nsIHandlerInfo)
/* nsIUTF8StringEnumerator getFileExtensions (); */
NS_IMETHODIMP ProxyMIMEInfo::GetFileExtensions(nsIUTF8StringEnumerator * *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void setFileExtensions (in AUTF8String aExtensions); */
NS_IMETHODIMP ProxyMIMEInfo::SetFileExtensions(const nsACString & aExtensions)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* boolean extensionExists (in AUTF8String aExtension); */
NS_IMETHODIMP ProxyMIMEInfo::ExtensionExists(const nsACString & aExtension, bool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void appendExtension (in AUTF8String aExtension); */
NS_IMETHODIMP ProxyMIMEInfo::AppendExtension(const nsACString & aExtension)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* attribute AUTF8String primaryExtension; */
NS_IMETHODIMP ProxyMIMEInfo::GetPrimaryExtension(nsACString & aPrimaryExtension)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP ProxyMIMEInfo::SetPrimaryExtension(const nsACString & aPrimaryExtension)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute ACString MIMEType; */
NS_IMETHODIMP ProxyMIMEInfo::GetMIMEType(nsACString & aMIMEType)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* boolean equals (in nsIMIMEInfo aMIMEInfo); */
NS_IMETHODIMP ProxyMIMEInfo::Equals(nsIMIMEInfo *aMIMEInfo, bool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute nsIArray possibleLocalHandlers; */
NS_IMETHODIMP ProxyMIMEInfo::GetPossibleLocalHandlers(nsIArray * *aPossibleLocalHandlers)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void launchWithFile (in nsIFile aFile); */
NS_IMETHODIMP ProxyMIMEInfo::LaunchWithFile(nsIFile *aFile)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
static already_AddRefed<nsIHandlerInfo> WrapHandlerInfo(const HandlerInfo& aHandlerInfo) {
nsCOMPtr<nsIHandlerInfo> info;
if (aHandlerInfo.isMIMEInfo()) {
info = new ProxyMIMEInfo(aHandlerInfo);
} else {
info = new ProxyHandlerInfo(aHandlerInfo);
}
return info.forget();
}
} // anonymous namespace
HandlerServiceParent::HandlerServiceParent()
{
}
HandlerServiceParent::~HandlerServiceParent()
{
}
bool HandlerServiceParent::RecvFillHandlerInfo(const HandlerInfo& aHandlerInfoData,
const nsCString& aOverrideType,
HandlerInfo* handlerInfoData)
{
nsCOMPtr<nsIHandlerInfo> info(WrapHandlerInfo(aHandlerInfoData));
nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
handlerSvc->FillHandlerInfo(info, aOverrideType);
ContentHandlerService::nsIHandlerInfoToHandlerInfo(info, handlerInfoData);
return true;
}
bool HandlerServiceParent::RecvExists(const HandlerInfo& aHandlerInfo,
bool* exists)
{
nsCOMPtr<nsIHandlerInfo> info(WrapHandlerInfo(aHandlerInfo));
nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
handlerSvc->Exists(info, exists);
return true;
}
bool HandlerServiceParent::RecvGetTypeFromExtension(const nsCString& aFileExtension,
nsCString* type)
{
nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
handlerSvc->GetTypeFromExtension(aFileExtension, *type);
return true;
}
void HandlerServiceParent::ActorDestroy(ActorDestroyReason aWhy)
{
}

View file

@ -0,0 +1,31 @@
#ifndef handler_service_parent_h
#define handler_service_parent_h
#include "mozilla/dom/PHandlerServiceParent.h"
#include "nsIMIMEInfo.h"
class nsIHandlerApp;
class HandlerServiceParent final : public mozilla::dom::PHandlerServiceParent
{
public:
HandlerServiceParent();
NS_INLINE_DECL_REFCOUNTING(HandlerServiceParent)
private:
virtual ~HandlerServiceParent();
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual bool RecvFillHandlerInfo(const HandlerInfo& aHandlerInfoData,
const nsCString& aOverrideType,
HandlerInfo* handlerInfoData) override;
virtual bool RecvExists(const HandlerInfo& aHandlerInfo,
bool* exits) override;
virtual bool RecvGetTypeFromExtension(const nsCString& aFileExtension,
nsCString* type) override;
};
#endif

View file

@ -0,0 +1,29 @@
/* 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 protocol PContent;
include protocol PChannelDiverter;
namespace mozilla {
namespace dom {
protocol PExternalHelperApp
{
manager PContent;
parent:
async OnStartRequest(nsCString entityID);
async OnDataAvailable(nsCString data, uint64_t offset, uint32_t count);
async OnStopRequest(nsresult code);
async DivertToParentUsing(PChannelDiverter diverter);
child:
async Cancel(nsresult aStatus);
async __delete__();
};
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,42 @@
/* 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 protocol PContent;
namespace mozilla {
namespace dom {
struct HandlerApp {
nsString name;
nsString detailedDescription;
};
struct HandlerInfo {
nsCString type;
bool isMIMEInfo;
nsString description;
bool alwaysAskBeforeHandling;
HandlerApp preferredApplicationHandler;
HandlerApp[] possibleApplicationHandlers;
long preferredAction;
};
sync protocol PHandlerService
{
manager PContent;
parent:
sync FillHandlerInfo(HandlerInfo aHandlerInfoData,
nsCString aOverrideType)
returns (HandlerInfo handlerInfoData);
sync Exists(HandlerInfo aHandlerInfo)
returns (bool exists);
sync GetTypeFromExtension(nsCString aFileExtension)
returns (nsCString type);
async __delete__();
};
} // namespace dom
} // namespace mozilla

View file

@ -0,0 +1,91 @@
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 "nsAndroidHandlerApp.h"
#include "GeneratedJNIWrappers.h"
using namespace mozilla;
NS_IMPL_ISUPPORTS(nsAndroidHandlerApp, nsIHandlerApp, nsISharingHandlerApp)
nsAndroidHandlerApp::nsAndroidHandlerApp(const nsAString& aName,
const nsAString& aDescription,
const nsAString& aPackageName,
const nsAString& aClassName,
const nsACString& aMimeType,
const nsAString& aAction) :
mName(aName), mDescription(aDescription), mPackageName(aPackageName),
mClassName(aClassName), mMimeType(aMimeType), mAction(aAction)
{
}
nsAndroidHandlerApp::~nsAndroidHandlerApp()
{
}
NS_IMETHODIMP
nsAndroidHandlerApp::GetName(nsAString & aName)
{
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP
nsAndroidHandlerApp::SetName(const nsAString & aName)
{
mName.Assign(aName);
return NS_OK;
}
NS_IMETHODIMP
nsAndroidHandlerApp::GetDetailedDescription(nsAString & aDescription)
{
aDescription.Assign(mDescription);
return NS_OK;
}
NS_IMETHODIMP
nsAndroidHandlerApp::SetDetailedDescription(const nsAString & aDescription)
{
mDescription.Assign(aDescription);
return NS_OK;
}
// XXX Workaround for bug 986975 to maintain the existing broken semantics
template<>
struct nsISharingHandlerApp::COMTypeInfo<nsAndroidHandlerApp, void> {
static const nsIID kIID;
};
const nsIID nsISharingHandlerApp::COMTypeInfo<nsAndroidHandlerApp, void>::kIID = NS_IHANDLERAPP_IID;
NS_IMETHODIMP
nsAndroidHandlerApp::Equals(nsIHandlerApp *aHandlerApp, bool *aRetval)
{
nsCOMPtr<nsAndroidHandlerApp> aApp = do_QueryInterface(aHandlerApp);
*aRetval = aApp && aApp->mName.Equals(mName) &&
aApp->mDescription.Equals(mDescription);
return NS_OK;
}
NS_IMETHODIMP
nsAndroidHandlerApp::LaunchWithURI(nsIURI *aURI, nsIInterfaceRequestor *aWindowContext)
{
nsCString uriSpec;
aURI->GetSpec(uriSpec);
return java::GeckoAppShell::OpenUriExternal(
uriSpec, mMimeType, mPackageName, mClassName,
mAction, EmptyString()) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsAndroidHandlerApp::Share(const nsAString & data, const nsAString & title)
{
return java::GeckoAppShell::OpenUriExternal(
data, mMimeType, mPackageName, mClassName,
mAction, EmptyString()) ? NS_OK : NS_ERROR_FAILURE;
}

View file

@ -0,0 +1,33 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 nsAndroidHandlerApp_h
#define nsAndroidHandlerApp_h
#include "nsMIMEInfoImpl.h"
#include "nsIExternalSharingAppService.h"
class nsAndroidHandlerApp : public nsISharingHandlerApp {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
NS_DECL_NSISHARINGHANDLERAPP
nsAndroidHandlerApp(const nsAString& aName, const nsAString& aDescription,
const nsAString& aPackageName,
const nsAString& aClassName,
const nsACString& aMimeType, const nsAString& aAction);
private:
virtual ~nsAndroidHandlerApp();
nsString mName;
nsString mDescription;
nsString mPackageName;
nsString mClassName;
nsCString mMimeType;
nsString mAction;
};
#endif

View file

@ -0,0 +1,61 @@
/* 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 "nsExternalSharingAppService.h"
#include "mozilla/ModuleUtils.h"
#include "nsIClassInfoImpl.h"
#include "AndroidBridge.h"
#include "nsArrayUtils.h"
#include "nsISupportsUtils.h"
#include "nsComponentManagerUtils.h"
using namespace mozilla;
NS_IMPL_ISUPPORTS(nsExternalSharingAppService, nsIExternalSharingAppService)
nsExternalSharingAppService::nsExternalSharingAppService()
{
}
nsExternalSharingAppService::~nsExternalSharingAppService()
{
}
NS_IMETHODIMP
nsExternalSharingAppService::ShareWithDefault(const nsAString & data,
const nsAString & mime,
const nsAString & title)
{
NS_NAMED_LITERAL_STRING(sendAction, "android.intent.action.SEND");
const nsString emptyString = EmptyString();
return java::GeckoAppShell::OpenUriExternal(data,
mime, emptyString, emptyString, sendAction, title) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsExternalSharingAppService::GetSharingApps(const nsAString & aMIMEType,
uint32_t *aLen,
nsISharingHandlerApp ***aHandlers)
{
nsresult rv;
NS_NAMED_LITERAL_STRING(sendAction, "android.intent.action.SEND");
nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (!AndroidBridge::Bridge())
return NS_OK;
AndroidBridge::Bridge()->GetHandlersForMimeType(aMIMEType, array,
nullptr, sendAction);
array->GetLength(aLen);
*aHandlers =
static_cast<nsISharingHandlerApp**>(moz_xmalloc(sizeof(nsISharingHandlerApp*)
* *aLen));
for (uint32_t i = 0; i < *aLen; i++) {
rv = array->QueryElementAt(i, NS_GET_IID(nsISharingHandlerApp),
(void**)(*aHandlers + i));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}

View file

@ -0,0 +1,28 @@
/* 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 NS_EXTERNAL_SHARING_APP_SERVICE_H
#define NS_EXTERNAL_SHARING_APP_SERVICE_H
#include "nsIExternalSharingAppService.h"
#define NS_EXTERNALSHARINGAPPSERVICE_CID \
{0x93e2c46e, 0x0011, 0x434b, \
{0x81, 0x2e, 0xb6, 0xf3, 0xa8, 0x1e, 0x2a, 0x58}}
class nsExternalSharingAppService final
: public nsIExternalSharingAppService
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEXTERNALSHARINGAPPSERVICE
nsExternalSharingAppService();
private:
~nsExternalSharingAppService();
};
#endif /*NS_EXTERNAL_SHARING_APP_SERVICE_H */

View file

@ -0,0 +1,27 @@
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 "nsExternalURLHandlerService.h"
#include "nsMIMEInfoAndroid.h"
NS_IMPL_ISUPPORTS(nsExternalURLHandlerService, nsIExternalURLHandlerService)
nsExternalURLHandlerService::nsExternalURLHandlerService()
{
}
nsExternalURLHandlerService::~nsExternalURLHandlerService()
{
}
NS_IMETHODIMP
nsExternalURLHandlerService::GetURLHandlerInfoFromOS(nsIURI *aURL,
bool *found,
nsIHandlerInfo **info)
{
nsCString uriSpec;
aURL->GetSpec(uriSpec);
return nsMIMEInfoAndroid::GetMimeInfoForURL(uriSpec, found, info);
}

View file

@ -0,0 +1,27 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 NSEXTERNALURLHANDLERSERVICE_H
#define NSEXTERNALURLHANDLERSERVICE_H
#include "nsIExternalURLHandlerService.h"
// {4BF1F8EF-D947-4BA3-9CD3-8C9A54A63A1C}
#define NS_EXTERNALURLHANDLERSERVICE_CID \
{0x4bf1f8ef, 0xd947, 0x4ba3, {0x9c, 0xd3, 0x8c, 0x9a, 0x54, 0xa6, 0x3a, 0x1c}}
class nsExternalURLHandlerService final
: public nsIExternalURLHandlerService
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEXTERNALURLHANDLERSERVICE
nsExternalURLHandlerService();
private:
~nsExternalURLHandlerService();
};
#endif // NSEXTERNALURLHANDLERSERVICE_H

View file

@ -0,0 +1,427 @@
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 "nsMIMEInfoAndroid.h"
#include "AndroidBridge.h"
#include "nsAndroidHandlerApp.h"
#include "nsArrayUtils.h"
#include "nsISupportsUtils.h"
#include "nsStringEnumerator.h"
#include "nsNetUtil.h"
using namespace mozilla;
NS_IMPL_ISUPPORTS(nsMIMEInfoAndroid, nsIMIMEInfo, nsIHandlerInfo)
NS_IMETHODIMP
nsMIMEInfoAndroid::LaunchDefaultWithFile(nsIFile* aFile)
{
return LaunchWithFile(aFile);
}
NS_IMETHODIMP
nsMIMEInfoAndroid::LoadUriInternal(nsIURI * aURI)
{
nsCString uriSpec;
aURI->GetSpec(uriSpec);
nsCString uriScheme;
aURI->GetScheme(uriScheme);
nsAutoString mimeType;
if (mType.Equals(uriScheme) || mType.Equals(uriSpec)) {
mimeType = EmptyString();
} else {
mimeType = NS_ConvertUTF8toUTF16(mType);
}
if (java::GeckoAppShell::OpenUriExternal(
NS_ConvertUTF8toUTF16(uriSpec), mimeType, EmptyString(),
EmptyString(), EmptyString(), EmptyString())) {
return NS_OK;
}
return NS_ERROR_FAILURE;
}
bool
nsMIMEInfoAndroid::GetMimeInfoForMimeType(const nsACString& aMimeType,
nsMIMEInfoAndroid** aMimeInfo)
{
RefPtr<nsMIMEInfoAndroid> info = new nsMIMEInfoAndroid(aMimeType);
mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge();
// we don't have access to the bridge, so just assume we can handle
// the mime type for now and let the system deal with it
if (!bridge){
info.forget(aMimeInfo);
return false;
}
nsIHandlerApp* systemDefault = nullptr;
if (!IsUTF8(aMimeType, true))
return false;
NS_ConvertUTF8toUTF16 mimeType(aMimeType);
bridge->GetHandlersForMimeType(mimeType,
info->mHandlerApps, &systemDefault);
if (systemDefault)
info->mPrefApp = systemDefault;
nsAutoCString fileExt;
bridge->GetExtensionFromMimeType(aMimeType, fileExt);
info->SetPrimaryExtension(fileExt);
uint32_t len;
info->mHandlerApps->GetLength(&len);
if (len == 1) {
info.forget(aMimeInfo);
return false;
}
info.forget(aMimeInfo);
return true;
}
bool
nsMIMEInfoAndroid::GetMimeInfoForFileExt(const nsACString& aFileExt,
nsMIMEInfoAndroid **aMimeInfo)
{
nsCString mimeType;
if (mozilla::AndroidBridge::Bridge())
mozilla::AndroidBridge::Bridge()->
GetMimeTypeFromExtensions(aFileExt, mimeType);
// "*/*" means that the bridge didn't know.
if (mimeType.Equals(nsDependentCString("*/*"), nsCaseInsensitiveCStringComparator()))
return false;
bool found = GetMimeInfoForMimeType(mimeType, aMimeInfo);
(*aMimeInfo)->SetPrimaryExtension(aFileExt);
return found;
}
/**
* Returns MIME info for the aURL, which may contain the whole URL or only a protocol
*/
nsresult
nsMIMEInfoAndroid::GetMimeInfoForURL(const nsACString &aURL,
bool *found,
nsIHandlerInfo **info)
{
nsMIMEInfoAndroid *mimeinfo = new nsMIMEInfoAndroid(aURL);
NS_ADDREF(*info = mimeinfo);
*found = true;
mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge();
if (!bridge) {
// we don't have access to the bridge, so just assume we can handle
// the protocol for now and let the system deal with it
return NS_OK;
}
nsIHandlerApp* systemDefault = nullptr;
bridge->GetHandlersForURL(NS_ConvertUTF8toUTF16(aURL),
mimeinfo->mHandlerApps, &systemDefault);
if (systemDefault)
mimeinfo->mPrefApp = systemDefault;
nsAutoCString fileExt;
nsAutoCString mimeType;
mimeinfo->GetType(mimeType);
bridge->GetExtensionFromMimeType(mimeType, fileExt);
mimeinfo->SetPrimaryExtension(fileExt);
uint32_t len;
mimeinfo->mHandlerApps->GetLength(&len);
if (len == 1) {
// Code that calls this requires an object regardless if the OS has
// something for us, so we return the empty object.
*found = false;
return NS_OK;
}
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetType(nsACString& aType)
{
aType.Assign(mType);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetDescription(nsAString& aDesc)
{
aDesc.Assign(mDescription);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::SetDescription(const nsAString& aDesc)
{
mDescription.Assign(aDesc);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetPreferredApplicationHandler(nsIHandlerApp** aApp)
{
*aApp = mPrefApp;
NS_IF_ADDREF(*aApp);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::SetPreferredApplicationHandler(nsIHandlerApp* aApp)
{
mPrefApp = aApp;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetPossibleApplicationHandlers(nsIMutableArray **aHandlerApps)
{
if (!mHandlerApps)
mHandlerApps = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (!mHandlerApps)
return NS_ERROR_OUT_OF_MEMORY;
*aHandlerApps = mHandlerApps;
NS_IF_ADDREF(*aHandlerApps);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetHasDefaultHandler(bool* aHasDefault)
{
uint32_t len;
*aHasDefault = false;
if (!mHandlerApps)
return NS_OK;
if (NS_FAILED(mHandlerApps->GetLength(&len)))
return NS_OK;
if (len == 0)
return NS_OK;
*aHasDefault = true;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetDefaultDescription(nsAString& aDesc)
{
aDesc.Assign(EmptyString());
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::LaunchWithURI(nsIURI* aURI, nsIInterfaceRequestor* req)
{
return mPrefApp->LaunchWithURI(aURI, req);
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetPreferredAction(nsHandlerInfoAction* aPrefAction)
{
*aPrefAction = mPrefAction;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::SetPreferredAction(nsHandlerInfoAction aPrefAction)
{
mPrefAction = aPrefAction;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetAlwaysAskBeforeHandling(bool* aAlwaysAsk)
{
*aAlwaysAsk = mAlwaysAsk;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::SetAlwaysAskBeforeHandling(bool aAlwaysAsk)
{
mAlwaysAsk = aAlwaysAsk;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetFileExtensions(nsIUTF8StringEnumerator** aResult)
{
return NS_NewUTF8StringEnumerator(aResult, &mExtensions, this);
}
NS_IMETHODIMP
nsMIMEInfoAndroid::SetFileExtensions(const nsACString & aExtensions)
{
mExtensions.Clear();
nsCString extList(aExtensions);
int32_t breakLocation = -1;
while ( (breakLocation = extList.FindChar(',')) != -1)
{
mExtensions.AppendElement(Substring(extList.get(), extList.get() + breakLocation));
extList.Cut(0, breakLocation + 1);
}
if (!extList.IsEmpty())
mExtensions.AppendElement(extList);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::ExtensionExists(const nsACString & aExtension, bool *aRetVal)
{
NS_ASSERTION(!aExtension.IsEmpty(), "no extension");
nsCString mimeType;
if (mozilla::AndroidBridge::Bridge()) {
mozilla::AndroidBridge::Bridge()->
GetMimeTypeFromExtensions(aExtension, mimeType);
}
// "*/*" means the bridge didn't find anything (i.e., extension doesn't exist).
*aRetVal = !mimeType.Equals(nsDependentCString("*/*"), nsCaseInsensitiveCStringComparator());
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::AppendExtension(const nsACString & aExtension)
{
mExtensions.AppendElement(aExtension);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetPrimaryExtension(nsACString & aPrimaryExtension)
{
if (!mExtensions.Length())
return NS_ERROR_NOT_INITIALIZED;
aPrimaryExtension = mExtensions[0];
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::SetPrimaryExtension(const nsACString & aExtension)
{
uint32_t extCount = mExtensions.Length();
uint8_t i;
bool found = false;
for (i=0; i < extCount; i++) {
const nsCString& ext = mExtensions[i];
if (ext.Equals(aExtension, nsCaseInsensitiveCStringComparator())) {
found = true;
break;
}
}
if (found) {
mExtensions.RemoveElementAt(i);
}
mExtensions.InsertElementAt(0, aExtension);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetMIMEType(nsACString & aMIMEType)
{
aMIMEType.Assign(mType);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::Equals(nsIMIMEInfo *aMIMEInfo, bool *aRetVal)
{
if (!aMIMEInfo) return NS_ERROR_NULL_POINTER;
nsAutoCString type;
nsresult rv = aMIMEInfo->GetMIMEType(type);
if (NS_FAILED(rv)) return rv;
*aRetVal = mType.Equals(type);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::GetPossibleLocalHandlers(nsIArray * *aPossibleLocalHandlers)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMIMEInfoAndroid::LaunchWithFile(nsIFile *aFile)
{
nsCOMPtr<nsIURI> uri;
NS_NewFileURI(getter_AddRefs(uri), aFile);
return LoadUriInternal(uri);
}
nsMIMEInfoAndroid::nsMIMEInfoAndroid(const nsACString& aMIMEType) :
mType(aMIMEType), mAlwaysAsk(true),
mPrefAction(nsIMIMEInfo::useHelperApp)
{
mPrefApp = new nsMIMEInfoAndroid::SystemChooser(this);
nsresult rv;
mHandlerApps = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
mHandlerApps->AppendElement(mPrefApp, false);
}
NS_IMPL_ISUPPORTS(nsMIMEInfoAndroid::SystemChooser, nsIHandlerApp)
nsresult nsMIMEInfoAndroid::SystemChooser::GetName(nsAString & aName) {
aName.AssignLiteral(u"Android chooser");
return NS_OK;
}
nsresult
nsMIMEInfoAndroid::SystemChooser::SetName(const nsAString&) {
return NS_OK;
}
nsresult
nsMIMEInfoAndroid::SystemChooser::GetDetailedDescription(nsAString & aDesc) {
aDesc.AssignLiteral(u"Android's default handler application chooser");
return NS_OK;
}
nsresult
nsMIMEInfoAndroid::SystemChooser::SetDetailedDescription(const nsAString&) {
return NS_OK;
}
// XXX Workaround for bug 986975 to maintain the existing broken semantics
template<>
struct nsIHandlerApp::COMTypeInfo<nsMIMEInfoAndroid::SystemChooser, void> {
static const nsIID kIID;
};
const nsIID nsIHandlerApp::COMTypeInfo<nsMIMEInfoAndroid::SystemChooser, void>::kIID = NS_IHANDLERAPP_IID;
nsresult
nsMIMEInfoAndroid::SystemChooser::Equals(nsIHandlerApp *aHandlerApp, bool *aRetVal) {
nsCOMPtr<nsMIMEInfoAndroid::SystemChooser> info = do_QueryInterface(aHandlerApp);
if (info)
return mOuter->Equals(info->mOuter, aRetVal);
*aRetVal = false;
return NS_OK;
}
nsresult
nsMIMEInfoAndroid::SystemChooser::LaunchWithURI(nsIURI* aURI, nsIInterfaceRequestor*)
{
return mOuter->LoadUriInternal(aURI);
}

View file

@ -0,0 +1,60 @@
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 nsMIMEInfoAndroid_h
#define nsMIMEInfoAndroid_h
#include "nsMIMEInfoImpl.h"
#include "nsIMutableArray.h"
#include "nsAndroidHandlerApp.h"
class nsMIMEInfoAndroid final : public nsIMIMEInfo
{
public:
static MOZ_MUST_USE bool
GetMimeInfoForMimeType(const nsACString& aMimeType,
nsMIMEInfoAndroid** aMimeInfo);
static MOZ_MUST_USE bool
GetMimeInfoForFileExt(const nsACString& aFileExt,
nsMIMEInfoAndroid** aMimeInfo);
static MOZ_MUST_USE nsresult
GetMimeInfoForURL(const nsACString &aURL, bool *found,
nsIHandlerInfo **info);
NS_DECL_ISUPPORTS
NS_DECL_NSIMIMEINFO
NS_DECL_NSIHANDLERINFO
nsMIMEInfoAndroid(const nsACString& aMIMEType);
private:
~nsMIMEInfoAndroid() {}
virtual MOZ_MUST_USE nsresult LaunchDefaultWithFile(nsIFile* aFile);
virtual MOZ_MUST_USE nsresult LoadUriInternal(nsIURI *aURI);
nsCOMPtr<nsIMutableArray> mHandlerApps;
nsCString mType;
nsTArray<nsCString> mExtensions;
bool mAlwaysAsk;
nsHandlerInfoAction mPrefAction;
nsString mDescription;
nsCOMPtr<nsIHandlerApp> mPrefApp;
public:
class SystemChooser final : public nsIHandlerApp {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
SystemChooser(nsMIMEInfoAndroid* aOuter): mOuter(aOuter) {}
private:
~SystemChooser() {}
nsMIMEInfoAndroid* mOuter;
};
};
#endif /* nsMIMEInfoAndroid_h */

View file

@ -0,0 +1,67 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 "nsOSHelperAppService.h"
#include "nsMIMEInfoAndroid.h"
#include "AndroidBridge.h"
nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
{
}
nsOSHelperAppService::~nsOSHelperAppService()
{
}
already_AddRefed<nsIMIMEInfo>
nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
const nsACString& aFileExt,
bool* aFound)
{
RefPtr<nsMIMEInfoAndroid> mimeInfo;
*aFound = false;
if (!aMIMEType.IsEmpty())
*aFound =
nsMIMEInfoAndroid::GetMimeInfoForMimeType(aMIMEType,
getter_AddRefs(mimeInfo));
if (!*aFound)
*aFound =
nsMIMEInfoAndroid::GetMimeInfoForFileExt(aFileExt,
getter_AddRefs(mimeInfo));
// Code that calls this requires an object regardless if the OS has
// something for us, so we return the empty object.
if (!*aFound)
mimeInfo = new nsMIMEInfoAndroid(aMIMEType);
return mimeInfo.forget();
}
nsresult
nsOSHelperAppService::OSProtocolHandlerExists(const char* aScheme,
bool* aExists)
{
*aExists = mozilla::AndroidBridge::Bridge()->GetHandlersForURL(NS_ConvertUTF8toUTF16(aScheme));
return NS_OK;
}
nsresult nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
bool *found,
nsIHandlerInfo **info)
{
return nsMIMEInfoAndroid::GetMimeInfoForURL(aScheme, found, info);
}
nsIHandlerApp*
nsOSHelperAppService::CreateAndroidHandlerApp(const nsAString& aName,
const nsAString& aDescription,
const nsAString& aPackageName,
const nsAString& aClassName,
const nsACString& aMimeType,
const nsAString& aAction)
{
return new nsAndroidHandlerApp(aName, aDescription, aPackageName,
aClassName, aMimeType, aAction);
}

View file

@ -0,0 +1,40 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 nsOSHelperAppService_h
#define nsOSHelperAppService_h
#include "nsCExternalHandlerService.h"
#include "nsExternalHelperAppService.h"
class nsOSHelperAppService : public nsExternalHelperAppService
{
public:
nsOSHelperAppService();
virtual ~nsOSHelperAppService();
virtual already_AddRefed<nsIMIMEInfo>
GetMIMEInfoFromOS(const nsACString& aMIMEType,
const nsACString& aFileExt,
bool* aFound);
virtual MOZ_MUST_USE nsresult
OSProtocolHandlerExists(const char* aScheme,
bool* aExists);
NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
bool *found,
nsIHandlerInfo **_retval);
static nsIHandlerApp*
CreateAndroidHandlerApp(const nsAString& aName,
const nsAString& aDescription,
const nsAString& aPackageName,
const nsAString& aClassName,
const nsACString& aMimeType,
const nsAString& aAction = EmptyString());
};
#endif /* nsOSHelperAppService_h */

View file

@ -0,0 +1,56 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- */
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nsOSHelperAppService.h"
#include "nsMIMEInfoImpl.h"
class nsGonkMIMEInfo : public nsMIMEInfoImpl {
public:
nsGonkMIMEInfo(const nsACString& aMIMEType) : nsMIMEInfoImpl(aMIMEType) { }
protected:
virtual nsresult LoadUriInternal(nsIURI *aURI) {
return NS_ERROR_NOT_IMPLEMENTED;
}
};
nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
{
}
nsOSHelperAppService::~nsOSHelperAppService()
{
}
already_AddRefed<nsIMIMEInfo>
nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
const nsACString& aFileExt,
bool* aFound)
{
*aFound = false;
// Even if we return false for aFound, we need to return a working
// nsIMIMEInfo implementation that will be used by the caller.
RefPtr<nsGonkMIMEInfo> mimeInfo = new nsGonkMIMEInfo(aMIMEType);
return mimeInfo.forget();
}
nsresult
nsOSHelperAppService::OSProtocolHandlerExists(const char* aScheme,
bool* aExists)
{
*aExists = false;
return NS_OK;
}

View file

@ -0,0 +1,39 @@
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- */
/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef nsOSHelperAppService_h
#define nsOSHelperAppService_h
#include "nsCExternalHandlerService.h"
#include "nsExternalHelperAppService.h"
class nsOSHelperAppService : public nsExternalHelperAppService
{
public:
nsOSHelperAppService();
virtual ~nsOSHelperAppService();
virtual already_AddRefed<nsIMIMEInfo>
GetMIMEInfoFromOS(const nsACString& aMIMEType,
const nsACString& aFileExt,
bool* aFound);
virtual MOZ_MUST_USE nsresult
OSProtocolHandlerExists(const char* aScheme,
bool* aExists);
};
#endif /* nsOSHelperAppService_h */

View file

@ -0,0 +1,389 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsDecodeAppleFile.h"
#include "prmem.h"
#include "nsCRT.h"
NS_IMPL_ADDREF(nsDecodeAppleFile)
NS_IMPL_RELEASE(nsDecodeAppleFile)
NS_INTERFACE_MAP_BEGIN(nsDecodeAppleFile)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
NS_INTERFACE_MAP_END_THREADSAFE
nsDecodeAppleFile::nsDecodeAppleFile()
{
m_state = parseHeaders;
m_dataBufferLength = 0;
m_dataBuffer = (unsigned char*) PR_MALLOC(MAX_BUFFERSIZE);
m_entries = nullptr;
m_rfRefNum = -1;
m_totalDataForkWritten = 0;
m_totalResourceForkWritten = 0;
m_headerOk = false;
m_comment[0] = 0;
memset(&m_dates, 0, sizeof(m_dates));
memset(&m_finderInfo, 0, sizeof(m_dates));
memset(&m_finderExtraInfo, 0, sizeof(m_dates));
}
nsDecodeAppleFile::~nsDecodeAppleFile()
{
PR_FREEIF(m_dataBuffer);
if (m_entries)
delete [] m_entries;
}
NS_IMETHODIMP nsDecodeAppleFile::Initialize(nsIOutputStream *output, nsIFile *file)
{
m_output = output;
nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(file);
macFile->GetTargetFSSpec(&m_fsFileSpec);
m_offset = 0;
m_dataForkOffset = 0;
return NS_OK;
}
NS_IMETHODIMP nsDecodeAppleFile::Close(void)
{
nsresult rv;
rv = m_output->Close();
int32_t i;
if (m_rfRefNum != -1)
FSClose(m_rfRefNum);
/* Check if the file is complete and if it's the case, write file attributes */
if (m_headerOk)
{
bool dataOk = true; /* It's ok if the file doesn't have a datafork, therefore set it to true by default. */
if (m_headers.magic == APPLESINGLE_MAGIC)
{
for (i = 0; i < m_headers.entriesCount; i ++)
if (ENT_DFORK == m_entries[i].id)
{
dataOk = (bool)(m_totalDataForkWritten == m_entries[i].length);
break;
}
}
bool resourceOk = FALSE;
for (i = 0; i < m_headers.entriesCount; i ++)
if (ENT_RFORK == m_entries[i].id)
{
resourceOk = (bool)(m_totalResourceForkWritten == m_entries[i].length);
break;
}
if (dataOk && resourceOk)
{
HFileInfo *fpb;
CInfoPBRec cipbr;
fpb = (HFileInfo *) &cipbr;
fpb->ioVRefNum = m_fsFileSpec.vRefNum;
fpb->ioDirID = m_fsFileSpec.parID;
fpb->ioNamePtr = m_fsFileSpec.name;
fpb->ioFDirIndex = 0;
PBGetCatInfoSync(&cipbr);
/* set finder info */
memcpy(&fpb->ioFlFndrInfo, &m_finderInfo, sizeof (FInfo));
memcpy(&fpb->ioFlXFndrInfo, &m_finderExtraInfo, sizeof (FXInfo));
fpb->ioFlFndrInfo.fdFlags &= 0xfc00; /* clear flags maintained by finder */
/* set file dates */
fpb->ioFlCrDat = m_dates.create - CONVERT_TIME;
fpb->ioFlMdDat = m_dates.modify - CONVERT_TIME;
fpb->ioFlBkDat = m_dates.backup - CONVERT_TIME;
/* update file info */
fpb->ioDirID = fpb->ioFlParID;
PBSetCatInfoSync(&cipbr);
/* set comment */
IOParam vinfo;
GetVolParmsInfoBuffer vp;
DTPBRec dtp;
memset((void *) &vinfo, 0, sizeof (vinfo));
vinfo.ioVRefNum = fpb->ioVRefNum;
vinfo.ioBuffer = (Ptr) &vp;
vinfo.ioReqCount = sizeof (vp);
if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr && ((vp.vMAttrib >> bHasDesktopMgr) & 1))
{
memset((void *) &dtp, 0, sizeof (dtp));
dtp.ioVRefNum = fpb->ioVRefNum;
if (PBDTGetPath(&dtp) == noErr)
{
dtp.ioDTBuffer = (Ptr) &m_comment[1];
dtp.ioNamePtr = fpb->ioNamePtr;
dtp.ioDirID = fpb->ioDirID;
dtp.ioDTReqCount = m_comment[0];
if (PBDTSetCommentSync(&dtp) == noErr)
PBDTFlushSync(&dtp);
}
}
}
}
return rv;
}
NS_IMETHODIMP nsDecodeAppleFile::Flush(void)
{
return m_output->Flush();
}
NS_IMETHODIMP nsDecodeAppleFile::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
{
return m_output->WriteFrom(inStr, count, _retval);
}
NS_IMETHODIMP nsDecodeAppleFile::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
{
return m_output->WriteSegments(reader, closure, count, _retval);
}
NS_IMETHODIMP nsDecodeAppleFile::IsNonBlocking(bool *aNonBlocking)
{
return m_output->IsNonBlocking(aNonBlocking);
}
NS_IMETHODIMP nsDecodeAppleFile::Write(const char *buffer, uint32_t bufferSize, uint32_t* writeCount)
{
/* WARNING: to simplify my life, I presume that I should get all appledouble headers in the first block,
else I would have to implement a buffer */
const char * buffPtr = buffer;
uint32_t dataCount;
int32_t i;
nsresult rv = NS_OK;
*writeCount = 0;
while (bufferSize > 0 && NS_SUCCEEDED(rv))
{
switch (m_state)
{
case parseHeaders :
dataCount = sizeof(ap_header) - m_dataBufferLength;
if (dataCount > bufferSize)
dataCount = bufferSize;
memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
m_dataBufferLength += dataCount;
if (m_dataBufferLength == sizeof(ap_header))
{
memcpy(&m_headers, m_dataBuffer, sizeof(ap_header));
/* Check header to be sure we are dealing with the right kind of data, else just write it to the data fork. */
if ((m_headers.magic == APPLEDOUBLE_MAGIC || m_headers.magic == APPLESINGLE_MAGIC) &&
m_headers.version == VERSION && m_headers.entriesCount)
{
/* Just to be sure, the filler must contains only 0 */
for (i = 0; i < 4 && m_headers.fill[i] == 0L; i ++)
;
if (i == 4)
m_state = parseEntries;
}
m_dataBufferLength = 0;
if (m_state == parseHeaders)
{
dataCount = 0;
m_state = parseWriteThrough;
}
}
break;
case parseEntries :
if (!m_entries)
{
m_entries = new ap_entry[m_headers.entriesCount];
if (!m_entries)
return NS_ERROR_OUT_OF_MEMORY;
}
uint32_t entriesSize = sizeof(ap_entry) * m_headers.entriesCount;
dataCount = entriesSize - m_dataBufferLength;
if (dataCount > bufferSize)
dataCount = bufferSize;
memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
m_dataBufferLength += dataCount;
if (m_dataBufferLength == entriesSize)
{
for (i = 0; i < m_headers.entriesCount; i ++)
{
memcpy(&m_entries[i], &m_dataBuffer[i * sizeof(ap_entry)], sizeof(ap_entry));
if (m_headers.magic == APPLEDOUBLE_MAGIC)
{
uint32_t offset = m_entries[i].offset + m_entries[i].length;
if (offset > m_dataForkOffset)
m_dataForkOffset = offset;
}
}
m_headerOk = true;
m_state = parseLookupPart;
}
break;
case parseLookupPart :
/* which part are we parsing? */
m_currentPartID = -1;
for (i = 0; i < m_headers.entriesCount; i ++)
if (m_offset == m_entries[i].offset && m_entries[i].length)
{
m_currentPartID = m_entries[i].id;
m_currentPartLength = m_entries[i].length;
m_currentPartCount = 0;
switch (m_currentPartID)
{
case ENT_DFORK : m_state = parseDataFork; break;
case ENT_RFORK : m_state = parseResourceFork; break;
case ENT_COMMENT :
case ENT_DATES :
case ENT_FINFO :
m_dataBufferLength = 0;
m_state = parsePart;
break;
default : m_state = parseSkipPart; break;
}
break;
}
if (m_currentPartID == -1)
{
/* maybe is the datafork of an appledouble file? */
if (m_offset == m_dataForkOffset)
{
m_currentPartID = ENT_DFORK;
m_currentPartLength = -1;
m_currentPartCount = 0;
m_state = parseDataFork;
}
else
dataCount = 1;
}
break;
case parsePart :
dataCount = m_currentPartLength - m_dataBufferLength;
if (dataCount > bufferSize)
dataCount = bufferSize;
memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
m_dataBufferLength += dataCount;
if (m_dataBufferLength == m_currentPartLength)
{
switch (m_currentPartID)
{
case ENT_COMMENT :
m_comment[0] = m_currentPartLength > 255 ? 255 : m_currentPartLength;
memcpy(&m_comment[1], buffPtr, m_comment[0]);
break;
case ENT_DATES :
if (m_currentPartLength == sizeof(m_dates))
memcpy(&m_dates, buffPtr, m_currentPartLength);
break;
case ENT_FINFO :
if (m_currentPartLength == (sizeof(m_finderInfo) + sizeof(m_finderExtraInfo)))
{
memcpy(&m_finderInfo, buffPtr, sizeof(m_finderInfo));
memcpy(&m_finderExtraInfo, buffPtr + sizeof(m_finderInfo), sizeof(m_finderExtraInfo));
}
break;
}
m_state = parseLookupPart;
}
break;
case parseSkipPart :
dataCount = m_currentPartLength - m_currentPartCount;
if (dataCount > bufferSize)
dataCount = bufferSize;
else
m_state = parseLookupPart;
break;
case parseDataFork :
if (m_headers.magic == APPLEDOUBLE_MAGIC)
dataCount = bufferSize;
else
{
dataCount = m_currentPartLength - m_currentPartCount;
if (dataCount > bufferSize)
dataCount = bufferSize;
else
m_state = parseLookupPart;
}
if (m_output)
{
uint32_t writeCount;
rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
if (dataCount != writeCount)
rv = NS_ERROR_FAILURE;
m_totalDataForkWritten += dataCount;
}
break;
case parseResourceFork :
dataCount = m_currentPartLength - m_currentPartCount;
if (dataCount > bufferSize)
dataCount = bufferSize;
else
m_state = parseLookupPart;
if (m_rfRefNum == -1)
{
if (noErr != FSpOpenRF(&m_fsFileSpec, fsWrPerm, &m_rfRefNum))
return NS_ERROR_FAILURE;
}
long count = dataCount;
if (noErr != FSWrite(m_rfRefNum, &count, buffPtr) || count != dataCount)
return NS_ERROR_FAILURE;
m_totalResourceForkWritten += dataCount;
break;
case parseWriteThrough :
dataCount = bufferSize;
if (m_output)
{
uint32_t writeCount;
rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
if (dataCount != writeCount)
rv = NS_ERROR_FAILURE;
}
break;
}
if (dataCount)
{
*writeCount += dataCount;
bufferSize -= dataCount;
buffPtr += dataCount;
m_currentPartCount += dataCount;
m_offset += dataCount;
dataCount = 0;
}
}
return rv;
}

View file

@ -0,0 +1,118 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 nsDecodeAppleFile_h__
#define nsDecodeAppleFile_h__
#include "nscore.h"
#include "nsCOMPtr.h"
#include "nsIFile.h"
#include "nsILocalFileMac.h"
#include "nsIOutputStream.h"
/*
** applefile definitions used
*/
#if PRAGMA_STRUCT_ALIGN
#pragma options align=mac68k
#endif
#define APPLESINGLE_MAGIC 0x00051600L
#define APPLEDOUBLE_MAGIC 0x00051607L
#define VERSION 0x00020000
#define NUM_ENTRIES 6
#define ENT_DFORK 1L
#define ENT_RFORK 2L
#define ENT_NAME 3L
#define ENT_COMMENT 4L
#define ENT_DATES 8L
#define ENT_FINFO 9L
#define CONVERT_TIME 1265437696L
/*
** data type used in the header decoder.
*/
typedef struct ap_header
{
int32_t magic;
int32_t version;
int32_t fill[4];
int16_t entriesCount;
} ap_header;
typedef struct ap_entry
{
int32_t id;
int32_t offset;
int32_t length;
} ap_entry;
typedef struct ap_dates
{
int32_t create, modify, backup, access;
} ap_dates;
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#endif
/*
**Error codes
*/
enum {
errADNotEnoughData = -12099,
errADNotSupported,
errADBadVersion
};
class nsDecodeAppleFile : public nsIOutputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
nsDecodeAppleFile();
virtual ~nsDecodeAppleFile();
MOZ_MUST_USE nsresult Initialize(nsIOutputStream *output, nsIFile *file);
private:
#define MAX_BUFFERSIZE 1024
enum ParserState {parseHeaders, parseEntries, parseLookupPart, parsePart, parseSkipPart,
parseDataFork, parseResourceFork, parseWriteThrough};
nsCOMPtr<nsIOutputStream> m_output;
FSSpec m_fsFileSpec;
SInt16 m_rfRefNum;
unsigned char * m_dataBuffer;
int32_t m_dataBufferLength;
ParserState m_state;
ap_header m_headers;
ap_entry * m_entries;
int32_t m_offset;
int32_t m_dataForkOffset;
int32_t m_totalDataForkWritten;
int32_t m_totalResourceForkWritten;
bool m_headerOk;
int32_t m_currentPartID;
int32_t m_currentPartLength;
int32_t m_currentPartCount;
Str255 m_comment;
ap_dates m_dates;
FInfo m_finderInfo;
FXInfo m_finderExtraInfo;
};
#endif

View file

@ -0,0 +1,26 @@
/* 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 NSLOCALHANDLERAPPMAC_H_
#define NSLOCALHANDLERAPPMAC_H_
#include "nsLocalHandlerApp.h"
class nsLocalHandlerAppMac : public nsLocalHandlerApp {
public:
nsLocalHandlerAppMac() { }
nsLocalHandlerAppMac(const char16_t *aName, nsIFile *aExecutable)
: nsLocalHandlerApp(aName, aExecutable) {}
nsLocalHandlerAppMac(const nsAString & aName, nsIFile *aExecutable)
: nsLocalHandlerApp(aName, aExecutable) {}
virtual ~nsLocalHandlerAppMac() { }
NS_IMETHOD LaunchWithURI(nsIURI* aURI, nsIInterfaceRequestor* aWindowContext);
NS_IMETHOD GetName(nsAString& aName);
};
#endif /*NSLOCALHANDLERAPPMAC_H_*/

View file

@ -0,0 +1,84 @@
/* 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/. */
#import <CoreFoundation/CoreFoundation.h>
#import <ApplicationServices/ApplicationServices.h>
#include "nsObjCExceptions.h"
#include "nsLocalHandlerAppMac.h"
#include "nsILocalFileMac.h"
#include "nsIURI.h"
// We override this to make sure app bundles display their pretty name (without .app suffix)
NS_IMETHODIMP nsLocalHandlerAppMac::GetName(nsAString& aName)
{
if (mExecutable) {
nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(mExecutable);
if (macFile) {
bool isPackage;
(void)macFile->IsPackage(&isPackage);
if (isPackage)
return macFile->GetBundleDisplayName(aName);
}
}
return nsLocalHandlerApp::GetName(aName);
}
/**
* mostly copy/pasted from nsMacShellService.cpp (which is in browser/,
* so we can't depend on it here). This code probably really wants to live
* somewhere more central (see bug 389922).
*/
NS_IMETHODIMP
nsLocalHandlerAppMac::LaunchWithURI(nsIURI *aURI,
nsIInterfaceRequestor *aWindowContext)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
nsresult rv;
nsCOMPtr<nsILocalFileMac> lfm(do_QueryInterface(mExecutable, &rv));
NS_ENSURE_SUCCESS(rv, rv);
CFURLRef appURL;
rv = lfm->GetCFURL(&appURL);
if (NS_FAILED(rv))
return rv;
nsAutoCString uriSpec;
aURI->GetAsciiSpec(uriSpec);
const UInt8* uriString = reinterpret_cast<const UInt8*>(uriSpec.get());
CFURLRef uri = ::CFURLCreateWithBytes(NULL, uriString, uriSpec.Length(),
kCFStringEncodingUTF8, NULL);
if (!uri) {
::CFRelease(appURL);
return NS_ERROR_OUT_OF_MEMORY;
}
CFArrayRef uris = ::CFArrayCreate(NULL, reinterpret_cast<const void**>(&uri),
1, NULL);
if (!uris) {
::CFRelease(uri);
::CFRelease(appURL);
return NS_ERROR_OUT_OF_MEMORY;
}
LSLaunchURLSpec launchSpec;
launchSpec.appURL = appURL;
launchSpec.itemURLs = uris;
launchSpec.passThruParams = NULL;
launchSpec.launchFlags = kLSLaunchDefaults;
launchSpec.asyncRefCon = NULL;
OSErr err = ::LSOpenFromURLSpec(&launchSpec, NULL);
::CFRelease(uris);
::CFRelease(uri);
::CFRelease(appURL);
return err != noErr ? NS_ERROR_FAILURE : NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}

View file

@ -0,0 +1,34 @@
/* 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 nsMIMEInfoMac_h_
#define nsMIMEInfoMac_h_
#include "nsMIMEInfoImpl.h"
class nsMIMEInfoMac : public nsMIMEInfoImpl {
public:
explicit nsMIMEInfoMac(const char* aMIMEType = "") : nsMIMEInfoImpl(aMIMEType) {}
explicit nsMIMEInfoMac(const nsACString& aMIMEType) : nsMIMEInfoImpl(aMIMEType) {}
nsMIMEInfoMac(const nsACString& aType, HandlerClass aClass) :
nsMIMEInfoImpl(aType, aClass) {}
NS_IMETHOD LaunchWithFile(nsIFile* aFile);
protected:
virtual MOZ_MUST_USE nsresult LoadUriInternal(nsIURI *aURI);
#ifdef DEBUG
virtual MOZ_MUST_USE nsresult LaunchDefaultWithFile(nsIFile* aFile) {
NS_NOTREACHED("do not call this method, use LaunchWithFile");
return NS_ERROR_UNEXPECTED;
}
#endif
static MOZ_MUST_USE nsresult OpenApplicationWithURI(nsIFile *aApplication,
const nsCString& aURI);
NS_IMETHOD GetDefaultDescription(nsAString& aDefaultDescription);
};
#endif

View file

@ -0,0 +1,114 @@
/* -*- Mode: C++; tab-width: 3; 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/. */
#import <ApplicationServices/ApplicationServices.h>
#include "nsObjCExceptions.h"
#include "nsMIMEInfoMac.h"
#include "nsILocalFileMac.h"
#include "nsIFileURL.h"
// We override this to make sure app bundles display their pretty name (without .app suffix)
NS_IMETHODIMP nsMIMEInfoMac::GetDefaultDescription(nsAString& aDefaultDescription)
{
if (mDefaultApplication) {
nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(mDefaultApplication);
if (macFile) {
bool isPackage;
(void)macFile->IsPackage(&isPackage);
if (isPackage)
return macFile->GetBundleDisplayName(aDefaultDescription);
}
}
return nsMIMEInfoImpl::GetDefaultDescription(aDefaultDescription);
}
NS_IMETHODIMP
nsMIMEInfoMac::LaunchWithFile(nsIFile *aFile)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
nsCOMPtr<nsIFile> application;
nsresult rv;
NS_ASSERTION(mClass == eMIMEInfo, "only MIME infos are currently allowed"
"to pass content by value");
if (mPreferredAction == useHelperApp) {
// we don't yet support passing content by value (rather than reference)
// to web apps. at some point, we will probably want to.
nsCOMPtr<nsILocalHandlerApp> localHandlerApp =
do_QueryInterface(mPreferredApplication, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = localHandlerApp->GetExecutable(getter_AddRefs(application));
NS_ENSURE_SUCCESS(rv, rv);
} else if (mPreferredAction == useSystemDefault) {
application = mDefaultApplication;
}
else
return NS_ERROR_INVALID_ARG;
// if we've already got an app, just QI so we have the launchWithDoc method
nsCOMPtr<nsILocalFileMac> app;
if (application) {
app = do_QueryInterface(application, &rv);
if (NS_FAILED(rv)) return rv;
} else {
// otherwise ask LaunchServices for an app directly
nsCOMPtr<nsILocalFileMac> tempFile = do_QueryInterface(aFile, &rv);
if (NS_FAILED(rv)) return rv;
FSRef tempFileRef;
tempFile->GetFSRef(&tempFileRef);
FSRef appFSRef;
if (::LSGetApplicationForItem(&tempFileRef, kLSRolesAll, &appFSRef, nullptr) == noErr)
{
app = (do_CreateInstance("@mozilla.org/file/local;1"));
if (!app) return NS_ERROR_FAILURE;
app->InitWithFSRef(&appFSRef);
} else {
return NS_ERROR_FAILURE;
}
}
return app->LaunchWithDoc(aFile, false);
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
nsresult
nsMIMEInfoMac::LoadUriInternal(nsIURI *aURI)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
NS_ENSURE_ARG_POINTER(aURI);
nsresult rv = NS_ERROR_FAILURE;
nsAutoCString uri;
aURI->GetSpec(uri);
if (!uri.IsEmpty()) {
CFURLRef myURLRef = ::CFURLCreateWithBytes(kCFAllocatorDefault,
(const UInt8*)uri.get(),
strlen(uri.get()),
kCFStringEncodingUTF8,
NULL);
if (myURLRef) {
OSStatus status = ::LSOpenCFURLRef(myURLRef, NULL);
if (status == noErr)
rv = NS_OK;
::CFRelease(myURLRef);
}
}
return rv;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}

View file

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 3; 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 nsOSHelperAppService_h__
#define nsOSHelperAppService_h__
// The OS helper app service is a subclass of nsExternalHelperAppService and is implemented on each
// platform. It contains platform specific code for finding helper applications for a given mime type
// in addition to launching those applications. This is the Mac version.
#include "nsExternalHelperAppService.h"
#include "nsCExternalHandlerService.h"
#include "nsMIMEInfoImpl.h"
#include "nsCOMPtr.h"
class nsOSHelperAppService : public nsExternalHelperAppService
{
public:
nsOSHelperAppService();
virtual ~nsOSHelperAppService();
// override nsIExternalProtocolService methods
NS_IMETHOD GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
// method overrides --> used to hook the mime service into internet config....
NS_IMETHOD GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo);
already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool * aFound);
NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
bool *found,
nsIHandlerInfo **_retval);
// GetFileTokenForPath must be implemented by each platform.
// platformAppPath --> a platform specific path to an application that we got out of the
// rdf data source. This can be a mac file spec, a unix path or a windows path depending on the platform
// aFile --> an nsIFile representation of that platform application path.
virtual MOZ_MUST_USE nsresult GetFileTokenForPath(const char16_t * platformAppPath, nsIFile ** aFile);
MOZ_MUST_USE nsresult OSProtocolHandlerExists(const char * aScheme,
bool * aHandlerExists);
private:
uint32_t mPermissions;
};
#endif // nsOSHelperAppService_h__

View file

@ -0,0 +1,569 @@
/* -*- Mode: C++; tab-width: 3; 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 <sys/types.h>
#include <sys/stat.h>
#include "nsOSHelperAppService.h"
#include "nsObjCExceptions.h"
#include "nsISupports.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsXPIDLString.h"
#include "nsIURL.h"
#include "nsIFile.h"
#include "nsILocalFileMac.h"
#include "nsMimeTypes.h"
#include "nsIStringBundle.h"
#include "nsIPromptService.h"
#include "nsMemory.h"
#include "nsCRT.h"
#include "nsMIMEInfoMac.h"
#include "nsEmbedCID.h"
#import <CoreFoundation/CoreFoundation.h>
#import <ApplicationServices/ApplicationServices.h>
// chrome URL's
#define HELPERAPPLAUNCHER_BUNDLE_URL "chrome://global/locale/helperAppLauncher.properties"
#define BRAND_BUNDLE_URL "chrome://branding/locale/brand.properties"
using mozilla::LogLevel;
/* This is an undocumented interface (in the Foundation framework) that has
* been stable since at least 10.2.8 and is still present on SnowLeopard.
* Furthermore WebKit has three public methods (in WebKitSystemInterface.h)
* that are thin wrappers around this interface's last three methods. So
* it's unlikely to change anytime soon. Now that we're no longer using
* Internet Config Services, this is the only way to look up a MIME type
* from an extension, or vice versa.
*/
@class NSURLFileTypeMappingsInternal;
@interface NSURLFileTypeMappings : NSObject
{
NSURLFileTypeMappingsInternal *_internal;
}
+ (NSURLFileTypeMappings*)sharedMappings;
- (NSString*)MIMETypeForExtension:(NSString*)aString;
- (NSString*)preferredExtensionForMIMEType:(NSString*)aString;
- (NSArray*)extensionsForMIMEType:(NSString*)aString;
@end
nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
{
mode_t mask = umask(0777);
umask(mask);
mPermissions = 0666 & ~mask;
}
nsOSHelperAppService::~nsOSHelperAppService()
{}
nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists)
{
// CFStringCreateWithBytes() can fail even if we're not out of memory --
// for example if the 'bytes' parameter is something very wierd (like "ÿÿ~"
// aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
// specified in the 'encoding' parameter. See bug 548719.
CFStringRef schemeString = ::CFStringCreateWithBytes(kCFAllocatorDefault,
(const UInt8*)aProtocolScheme,
strlen(aProtocolScheme),
kCFStringEncodingUTF8,
false);
if (schemeString) {
// LSCopyDefaultHandlerForURLScheme() can fail to find the default handler
// for aProtocolScheme when it's never been explicitly set (using
// LSSetDefaultHandlerForURLScheme()). For example, Safari is the default
// handler for the "http" scheme on a newly installed copy of OS X. But
// this (presumably) wasn't done using LSSetDefaultHandlerForURLScheme(),
// so LSCopyDefaultHandlerForURLScheme() will fail to find Safari. To get
// around this we use LSCopyAllHandlersForURLScheme() instead -- which seems
// never to fail.
// http://lists.apple.com/archives/Carbon-dev/2007/May/msg00349.html
// http://www.realsoftware.com/listarchives/realbasic-nug/2008-02/msg00119.html
CFArrayRef handlerArray = ::LSCopyAllHandlersForURLScheme(schemeString);
*aHandlerExists = !!handlerArray;
if (handlerArray)
::CFRelease(handlerArray);
::CFRelease(schemeString);
} else {
*aHandlerExists = false;
}
return NS_OK;
}
NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
nsresult rv = NS_ERROR_NOT_AVAILABLE;
CFStringRef schemeCFString =
::CFStringCreateWithBytes(kCFAllocatorDefault,
(const UInt8 *)PromiseFlatCString(aScheme).get(),
aScheme.Length(),
kCFStringEncodingUTF8,
false);
if (schemeCFString) {
CFStringRef lookupCFString = ::CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:"), schemeCFString);
if (lookupCFString) {
CFURLRef lookupCFURL = ::CFURLCreateWithString(NULL, lookupCFString, NULL);
if (lookupCFURL) {
CFURLRef appCFURL = NULL;
OSStatus theErr = ::LSGetApplicationForURL(lookupCFURL, kLSRolesAll, NULL, &appCFURL);
if (theErr == noErr) {
CFBundleRef handlerBundle = ::CFBundleCreate(NULL, appCFURL);
if (handlerBundle) {
// Get the human-readable name of the default handler bundle
CFStringRef bundleName =
(CFStringRef)::CFBundleGetValueForInfoDictionaryKey(handlerBundle,
kCFBundleNameKey);
if (bundleName) {
AutoTArray<UniChar, 255> buffer;
CFIndex bundleNameLength = ::CFStringGetLength(bundleName);
buffer.SetLength(bundleNameLength);
::CFStringGetCharacters(bundleName, CFRangeMake(0, bundleNameLength),
buffer.Elements());
_retval.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), bundleNameLength);
rv = NS_OK;
}
::CFRelease(handlerBundle);
}
::CFRelease(appCFURL);
}
::CFRelease(lookupCFURL);
}
::CFRelease(lookupCFString);
}
::CFRelease(schemeCFString);
}
return rv;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
nsresult nsOSHelperAppService::GetFileTokenForPath(const char16_t * aPlatformAppPath, nsIFile ** aFile)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
nsresult rv;
nsCOMPtr<nsILocalFileMac> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv,rv);
CFURLRef pathAsCFURL;
CFStringRef pathAsCFString = ::CFStringCreateWithCharacters(NULL,
reinterpret_cast<const UniChar*>(aPlatformAppPath),
NS_strlen(aPlatformAppPath));
if (!pathAsCFString)
return NS_ERROR_OUT_OF_MEMORY;
if (::CFStringGetCharacterAtIndex(pathAsCFString, 0) == '/') {
// we have a Posix path
pathAsCFURL = ::CFURLCreateWithFileSystemPath(nullptr, pathAsCFString,
kCFURLPOSIXPathStyle, false);
if (!pathAsCFURL) {
::CFRelease(pathAsCFString);
return NS_ERROR_OUT_OF_MEMORY;
}
}
else {
// if it doesn't start with a / it's not an absolute Posix path
// let's check if it's a HFS path left over from old preferences
// If it starts with a ':' char, it's not an absolute HFS path
// so bail for that, and also if it's empty
if (::CFStringGetLength(pathAsCFString) == 0 ||
::CFStringGetCharacterAtIndex(pathAsCFString, 0) == ':')
{
::CFRelease(pathAsCFString);
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
pathAsCFURL = ::CFURLCreateWithFileSystemPath(nullptr, pathAsCFString,
kCFURLHFSPathStyle, false);
if (!pathAsCFURL) {
::CFRelease(pathAsCFString);
return NS_ERROR_OUT_OF_MEMORY;
}
}
rv = localFile->InitWithCFURL(pathAsCFURL);
::CFRelease(pathAsCFString);
::CFRelease(pathAsCFURL);
if (NS_FAILED(rv))
return rv;
*aFile = localFile;
NS_IF_ADDREF(*aFile);
return NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
NS_IMETHODIMP nsOSHelperAppService::GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo)
{
return nsExternalHelperAppService::GetFromTypeAndExtension(aType, aFileExt, aMIMEInfo);
}
// Returns the MIME types an application bundle explicitly claims to handle.
// Returns NULL if aAppRef doesn't explicitly claim to handle any MIME types.
// If the return value is non-NULL, the caller is responsible for freeing it.
// This isn't necessarily the same as the MIME types the application bundle
// is registered to handle in the Launch Services database. (For example
// the Preview application is normally registered to handle the application/pdf
// MIME type, even though it doesn't explicitly claim to handle *any* MIME
// types in its Info.plist. This is probably because Preview does explicitly
// claim to handle the com.adobe.pdf UTI, and Launch Services somehow
// translates this into a claim to support the application/pdf MIME type.
// Launch Services doesn't provide any APIs (documented or undocumented) to
// query which MIME types a given application is registered to handle. So any
// app that wants this information (e.g. the Default Apps pref pane) needs to
// iterate through the entire Launch Services database -- a process which can
// take several seconds.)
static CFArrayRef GetMIMETypesHandledByApp(FSRef *aAppRef)
{
CFURLRef appURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aAppRef);
if (!appURL) {
return NULL;
}
CFDictionaryRef infoDict = ::CFBundleCopyInfoDictionaryForURL(appURL);
::CFRelease(appURL);
if (!infoDict) {
return NULL;
}
CFTypeRef cfObject = ::CFDictionaryGetValue(infoDict, CFSTR("CFBundleDocumentTypes"));
if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) {
::CFRelease(infoDict);
return NULL;
}
CFArrayRef docTypes = static_cast<CFArrayRef>(cfObject);
CFIndex docTypesCount = ::CFArrayGetCount(docTypes);
if (docTypesCount == 0) {
::CFRelease(infoDict);
return NULL;
}
CFMutableArrayRef mimeTypes =
::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
for (CFIndex i = 0; i < docTypesCount; ++i) {
cfObject = ::CFArrayGetValueAtIndex(docTypes, i);
if (!cfObject || (::CFGetTypeID(cfObject) != ::CFDictionaryGetTypeID())) {
continue;
}
CFDictionaryRef typeDict = static_cast<CFDictionaryRef>(cfObject);
// When this key is present (on OS X 10.5 and later), its contents
// take precedence over CFBundleTypeMIMETypes (and CFBundleTypeExtensions
// and CFBundleTypeOSTypes).
cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("LSItemContentTypes"));
if (cfObject && (::CFGetTypeID(cfObject) == ::CFArrayGetTypeID())) {
continue;
}
cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("CFBundleTypeMIMETypes"));
if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) {
continue;
}
CFArrayRef mimeTypeHolder = static_cast<CFArrayRef>(cfObject);
CFArrayAppendArray(mimeTypes, mimeTypeHolder,
::CFRangeMake(0, ::CFArrayGetCount(mimeTypeHolder)));
}
::CFRelease(infoDict);
if (!::CFArrayGetCount(mimeTypes)) {
::CFRelease(mimeTypes);
mimeTypes = NULL;
}
return mimeTypes;
}
// aMIMEType and aFileExt might not match, If they don't we set *aFound to
// false and return a minimal nsIMIMEInfo structure.
already_AddRefed<nsIMIMEInfo>
nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
const nsACString& aFileExt,
bool * aFound)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
*aFound = false;
const nsCString& flatType = PromiseFlatCString(aMIMEType);
const nsCString& flatExt = PromiseFlatCString(aFileExt);
MOZ_LOG(mLog, LogLevel::Debug, ("Mac: HelperAppService lookup for type '%s' ext '%s'\n",
flatType.get(), flatExt.get()));
// Create a Mac-specific MIME info so we can use Mac-specific members.
RefPtr<nsMIMEInfoMac> mimeInfoMac = new nsMIMEInfoMac(aMIMEType);
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
OSStatus err;
bool haveAppForType = false;
bool haveAppForExt = false;
bool typeAppIsDefault = false;
bool extAppIsDefault = false;
FSRef typeAppFSRef;
FSRef extAppFSRef;
CFStringRef cfMIMEType = NULL;
if (!aMIMEType.IsEmpty()) {
CFURLRef appURL = NULL;
// CFStringCreateWithCString() can fail even if we're not out of memory --
// for example if the 'cStr' parameter is something very weird (like "ÿÿ~"
// aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
// specified in the 'encoding' parameter. See bug 548719.
cfMIMEType = ::CFStringCreateWithCString(NULL, flatType.get(),
kCFStringEncodingUTF8);
if (cfMIMEType) {
err = ::LSCopyApplicationForMIMEType(cfMIMEType, kLSRolesAll, &appURL);
if ((err == noErr) && appURL && ::CFURLGetFSRef(appURL, &typeAppFSRef)) {
haveAppForType = true;
MOZ_LOG(mLog, LogLevel::Debug, ("LSCopyApplicationForMIMEType found a default application\n"));
}
if (appURL) {
::CFRelease(appURL);
}
}
}
if (!aFileExt.IsEmpty()) {
// CFStringCreateWithCString() can fail even if we're not out of memory --
// for example if the 'cStr' parameter is something very wierd (like "ÿÿ~"
// aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
// specified in the 'encoding' parameter. See bug 548719.
CFStringRef cfExt = ::CFStringCreateWithCString(NULL, flatExt.get(), kCFStringEncodingUTF8);
if (cfExt) {
err = ::LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, cfExt,
kLSRolesAll, &extAppFSRef, nullptr);
if (err == noErr) {
haveAppForExt = true;
MOZ_LOG(mLog, LogLevel::Debug, ("LSGetApplicationForInfo found a default application\n"));
}
::CFRelease(cfExt);
}
}
if (haveAppForType && haveAppForExt) {
// Do aMIMEType and aFileExt match?
if (::FSCompareFSRefs((const FSRef *) &typeAppFSRef, (const FSRef *) &extAppFSRef) == noErr) {
typeAppIsDefault = true;
*aFound = true;
}
} else if (haveAppForType) {
// If aFileExt isn't empty, it doesn't match aMIMEType.
if (aFileExt.IsEmpty()) {
typeAppIsDefault = true;
*aFound = true;
}
} else if (haveAppForExt) {
// If aMIMEType isn't empty, it doesn't match aFileExt, which should mean
// that we haven't found a matching app. But make an exception for an app
// that also explicitly claims to handle aMIMEType, or which doesn't claim
// to handle any MIME types. This helps work around the following Apple
// design flaw:
//
// Launch Services is somewhat unreliable about registering Apple apps to
// handle MIME types. Probably this is because Apple has officially
// deprecated support for MIME types (in favor of UTIs). As a result,
// most of Apple's own apps don't explicitly claim to handle any MIME
// types (instead they claim to handle one or more UTIs). So Launch
// Services must contain logic to translate support for a given UTI into
// support for one or more MIME types, and it doesn't always do this
// correctly. For example DiskImageMounter isn't (by default) registered
// to handle the application/x-apple-diskimage MIME type. See bug 675356.
//
// Apple has also deprecated support for file extensions, and Apple apps
// also don't register to handle them. But for some reason Launch Services
// is (apparently) better about translating support for a given UTI into
// support for one or more file extensions. It's not at all clear why.
if (aMIMEType.IsEmpty()) {
extAppIsDefault = true;
*aFound = true;
} else {
CFArrayRef extAppMIMETypes = GetMIMETypesHandledByApp(&extAppFSRef);
if (extAppMIMETypes) {
if (cfMIMEType) {
if (::CFArrayContainsValue(extAppMIMETypes,
::CFRangeMake(0, ::CFArrayGetCount(extAppMIMETypes)),
cfMIMEType)) {
extAppIsDefault = true;
*aFound = true;
}
}
::CFRelease(extAppMIMETypes);
} else {
extAppIsDefault = true;
*aFound = true;
}
}
}
if (cfMIMEType) {
::CFRelease(cfMIMEType);
}
if (aMIMEType.IsEmpty()) {
if (haveAppForExt) {
// If aMIMEType is empty and we've found a default app for aFileExt, try
// to get the MIME type from aFileExt. (It might also be worth doing
// this when aMIMEType isn't empty but haveAppForType is false -- but
// the doc for this method says that if we have a MIME type (in
// aMIMEType), we need to give it preference.)
NSURLFileTypeMappings *map = [NSURLFileTypeMappings sharedMappings];
NSString *extStr = [NSString stringWithCString:flatExt.get() encoding:NSASCIIStringEncoding];
NSString *typeStr = map ? [map MIMETypeForExtension:extStr] : NULL;
if (typeStr) {
nsAutoCString mimeType;
mimeType.Assign((char *)[typeStr cStringUsingEncoding:NSASCIIStringEncoding]);
mimeInfoMac->SetMIMEType(mimeType);
haveAppForType = true;
} else {
// Sometimes the OS won't give us a MIME type for an extension that's
// registered with Launch Services and has a default app: For example
// Real Player registers itself for the "ogg" extension and for the
// audio/x-ogg and application/x-ogg MIME types, but
// MIMETypeForExtension returns nil for the "ogg" extension even on
// systems where Real Player is installed. This is probably an Apple
// bug. But bad things happen if we return an nsIMIMEInfo structure
// with an empty MIME type and set *aFound to true. So in this
// case we need to set it to false here.
haveAppForExt = false;
extAppIsDefault = false;
*aFound = false;
}
} else {
// Otherwise set the MIME type to a reasonable fallback.
mimeInfoMac->SetMIMEType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
}
}
if (typeAppIsDefault || extAppIsDefault) {
if (haveAppForExt)
mimeInfoMac->AppendExtension(aFileExt);
nsCOMPtr<nsILocalFileMac> app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
if (!app) {
[localPool release];
return nullptr;
}
CFStringRef cfAppName = NULL;
if (typeAppIsDefault) {
app->InitWithFSRef(&typeAppFSRef);
::LSCopyItemAttribute((const FSRef *) &typeAppFSRef, kLSRolesAll,
kLSItemDisplayName, (CFTypeRef *) &cfAppName);
} else {
app->InitWithFSRef(&extAppFSRef);
::LSCopyItemAttribute((const FSRef *) &extAppFSRef, kLSRolesAll,
kLSItemDisplayName, (CFTypeRef *) &cfAppName);
}
if (cfAppName) {
AutoTArray<UniChar, 255> buffer;
CFIndex appNameLength = ::CFStringGetLength(cfAppName);
buffer.SetLength(appNameLength);
::CFStringGetCharacters(cfAppName, CFRangeMake(0, appNameLength),
buffer.Elements());
nsAutoString appName;
appName.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), appNameLength);
mimeInfoMac->SetDefaultDescription(appName);
::CFRelease(cfAppName);
}
mimeInfoMac->SetDefaultApplication(app);
mimeInfoMac->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
} else {
mimeInfoMac->SetPreferredAction(nsIMIMEInfo::saveToDisk);
}
nsAutoCString mimeType;
mimeInfoMac->GetMIMEType(mimeType);
if (*aFound && !mimeType.IsEmpty()) {
// If we have a MIME type, make sure its preferred extension is included
// in our list.
NSURLFileTypeMappings *map = [NSURLFileTypeMappings sharedMappings];
NSString *typeStr = [NSString stringWithCString:mimeType.get() encoding:NSASCIIStringEncoding];
NSString *extStr = map ? [map preferredExtensionForMIMEType:typeStr] : NULL;
if (extStr) {
nsAutoCString preferredExt;
preferredExt.Assign((char *)[extStr cStringUsingEncoding:NSASCIIStringEncoding]);
mimeInfoMac->AppendExtension(preferredExt);
}
CFStringRef cfType = ::CFStringCreateWithCString(NULL, mimeType.get(), kCFStringEncodingUTF8);
if (cfType) {
CFStringRef cfTypeDesc = NULL;
if (::LSCopyKindStringForMIMEType(cfType, &cfTypeDesc) == noErr) {
AutoTArray<UniChar, 255> buffer;
CFIndex typeDescLength = ::CFStringGetLength(cfTypeDesc);
buffer.SetLength(typeDescLength);
::CFStringGetCharacters(cfTypeDesc, CFRangeMake(0, typeDescLength),
buffer.Elements());
nsAutoString typeDesc;
typeDesc.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), typeDescLength);
mimeInfoMac->SetDescription(typeDesc);
}
if (cfTypeDesc) {
::CFRelease(cfTypeDesc);
}
::CFRelease(cfType);
}
}
MOZ_LOG(mLog, LogLevel::Debug, ("OS gave us: type '%s' found '%i'\n", mimeType.get(), *aFound));
[localPool release];
return mimeInfoMac.forget();
NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
}
NS_IMETHODIMP
nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
bool *found,
nsIHandlerInfo **_retval)
{
NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
found);
if (NS_FAILED(rv))
return rv;
nsMIMEInfoMac *handlerInfo =
new nsMIMEInfoMac(aScheme, nsMIMEInfoBase::eProtocolInfo);
NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*_retval = handlerInfo);
if (!*found) {
// Code that calls this requires an object regardless if the OS has
// something for us, so we return the empty object.
return NS_OK;
}
nsAutoString desc;
rv = GetApplicationDescription(aScheme, desc);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetApplicationDescription failed");
handlerInfo->SetDefaultDescription(desc);
return NS_OK;
}

View file

@ -0,0 +1,139 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
TEST_DIRS += ['tests']
XPIDL_SOURCES += [
'nsCExternalHandlerService.idl',
'nsIContentDispatchChooser.idl',
'nsIExternalHelperAppService.idl',
'nsIExternalProtocolService.idl',
'nsIExternalSharingAppService.idl',
'nsIExternalURLHandlerService.idl',
'nsIHandlerService.idl',
'nsIHelperAppLauncherDialog.idl',
]
XPIDL_MODULE = 'exthandler'
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
osdir = 'win'
LOCAL_INCLUDES += ['win']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
osdir = 'mac'
elif CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk', 'uikit'):
osdir = CONFIG['MOZ_WIDGET_TOOLKIT']
else:
osdir = 'unix'
EXPORTS += [
osdir + '/nsOSHelperAppService.h'
]
EXPORTS += [
'ContentHandlerService.h',
'nsExternalHelperAppService.h',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
EXPORTS += [ '%s/%s' % (osdir, f) for f in [
'nsExternalSharingAppService.h',
'nsExternalURLHandlerService.h',
]]
EXPORTS.mozilla.dom += [
'ExternalHelperAppChild.h',
'ExternalHelperAppParent.h',
]
UNIFIED_SOURCES += [
'ContentHandlerService.cpp',
'ExternalHelperAppChild.cpp',
'ExternalHelperAppParent.cpp',
'HandlerServiceParent.cpp',
'nsExternalHelperAppService.cpp',
'nsExternalProtocolHandler.cpp',
'nsLocalHandlerApp.cpp',
'nsMIMEInfoImpl.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
UNIFIED_SOURCES += [
'mac/nsLocalHandlerAppMac.mm',
'mac/nsMIMEInfoMac.mm',
'mac/nsOSHelperAppService.mm',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
UNIFIED_SOURCES += [
'uikit/nsLocalHandlerAppUIKit.mm',
'uikit/nsMIMEInfoUIKit.mm',
'uikit/nsOSHelperAppService.mm',
]
else:
# These files can't be built in unified mode because they redefine LOG.
SOURCES += [
osdir + '/nsOSHelperAppService.cpp',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
UNIFIED_SOURCES += [
'unix/nsGNOMERegistry.cpp',
'unix/nsMIMEInfoUnix.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
UNIFIED_SOURCES += [
'android/nsAndroidHandlerApp.cpp',
'android/nsExternalSharingAppService.cpp',
'android/nsExternalURLHandlerService.cpp',
'android/nsMIMEInfoAndroid.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
UNIFIED_SOURCES += [
'win/nsMIMEInfoWin.cpp',
]
if CONFIG['MOZ_ENABLE_DBUS']:
UNIFIED_SOURCES += [
'nsDBusHandlerApp.cpp',
]
if CONFIG['MOZ_ENABLE_CONTENTACTION']:
UNIFIED_SOURCES += [
'nsContentHandlerApp.cpp',
]
EXTRA_COMPONENTS += [
'nsHandlerService.js',
'nsHandlerService.manifest',
'nsWebHandlerApp.js',
'nsWebHandlerApp.manifest',
]
IPDL_SOURCES += [
'PExternalHelperApp.ipdl',
'PHandlerService.ipdl',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/base',
'/dom/ipc',
'/netwerk/base',
'/netwerk/protocol/http',
]
if CONFIG['MOZ_ENABLE_DBUS']:
CXXFLAGS += CONFIG['TK_CFLAGS']
CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
CXXFLAGS += CONFIG['TK_CFLAGS']
CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']

View file

@ -0,0 +1,58 @@
/* -*- Mode: IDL; tab-width: 3; 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 "nsIExternalHelperAppService.idl"
/*
nsCExternalHelperApp implements:
-------------------------
nsIExternalHelperAppService
*/
%{ C++
/* A7F800E0-4306-11d4-98D0-001083010E9B */
#define NS_EXTERNALHELPERAPPSERVICE_CID \
{ 0xa7f800e0, 0x4306, 0x11d4, { 0x98, 0xd0, 0x0, 0x10, 0x83, 0x1, 0xe, 0x9b } }
#define NS_EXTERNALHELPERAPPSERVICE_CONTRACTID \
"@mozilla.org/uriloader/external-helper-app-service;1"
#define NS_HANDLERSERVICE_CONTRACTID \
"@mozilla.org/uriloader/handler-service;1"
#define NS_EXTERNALPROTOCOLSERVICE_CONTRACTID \
"@mozilla.org/uriloader/external-protocol-service;1"
#define NS_MIMESERVICE_CONTRACTID \
"@mozilla.org/mime;1"
#define NS_EXTERNALPROTOCOLHANDLER_CID \
{ 0xbd6390c8, 0xfbea, 0x11d4, {0x98, 0xf6, 0x0, 0x10, 0x83, 0x1, 0xe, 0x9b } }
/* 9fa83ce7-d0ab-4ed3-938e-afafee435670 */
#define NS_BLOCKEDEXTERNALPROTOCOLHANDLER_CID \
{ 0x9fa83ce7, 0xd0ab, 0x4ed3, {0x93, 0x8e, 0xaf, 0xaf, 0xee, 0x43, 0x56, 0x70 } }
/* bc0017e3-2438-47be-a567-41db58f17627 */
#define NS_LOCALHANDLERAPP_CID \
{ 0xbc0017e3, 0x2438, 0x47be, {0xa5, 0x67, 0x41, 0xdb, 0x58, 0xf1, 0x76, 0x27 } }
/*6c3c274b-4cbf-4bb5-a635-05ad2cbb6535*/
#define NS_DBUSHANDLERAPP_CID \
{ 0x6c3c274b, 0x4cbf, 0x4bb5, {0xa6, 0x35, 0x05, 0xad, 0x2c, 0xbb, 0x65, 0x35 } }
#define NS_DBUSHANDLERAPP_CONTRACTID \
"@mozilla.org/uriloader/dbus-handler-app;1"
#define NS_LOCALHANDLERAPP_CONTRACTID \
"@mozilla.org/uriloader/local-handler-app;1"
#define NS_WEBHANDLERAPP_CONTRACTID \
"@mozilla.org/uriloader/web-handler-app;1"
%}

View file

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 "nsContentHandlerApp.h"
#include "nsIURI.h"
#include "nsIClassInfoImpl.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#define NS_CONTENTHANDLER_CID \
{ 0x43ec2c82, 0xb9db, 0x4835, {0x80, 0x3f, 0x64, 0xc9, 0x72, 0x5a, 0x70, 0x28 } }
NS_IMPL_CLASSINFO(nsContentHandlerApp, nullptr, 0, NS_CONTENTHANDLER_CID)
NS_IMPL_ISUPPORTS_CI(nsContentHandlerApp, nsIHandlerApp)
nsContentHandlerApp::nsContentHandlerApp(nsString aName, nsCString aType,
ContentAction::Action& aAction) :
mName(aName),
mType(aType),
mAction(aAction)
{
}
////////////////////////////////////////////////////////////////////////////////
//// nsIHandlerInfo
NS_IMETHODIMP nsContentHandlerApp::GetName(nsAString& aName)
{
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP nsContentHandlerApp::SetName(const nsAString& aName)
{
mName.Assign(aName);
return NS_OK;
}
NS_IMETHODIMP nsContentHandlerApp::Equals(nsIHandlerApp *aHandlerApp, bool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsContentHandlerApp::GetDetailedDescription(nsAString& aDetailedDescription)
{
aDetailedDescription.Assign(mDetailedDescription);
return NS_OK;
}
NS_IMETHODIMP nsContentHandlerApp::SetDetailedDescription(const nsAString& aDetailedDescription)
{
mDetailedDescription.Assign(aDetailedDescription);
return NS_OK;
}
NS_IMETHODIMP
nsContentHandlerApp::LaunchWithURI(nsIURI *aURI,
nsIInterfaceRequestor *aWindowContext)
{
nsAutoCString spec;
nsresult rv = aURI->GetAsciiSpec(spec);
NS_ENSURE_SUCCESS(rv,rv);
const char* url = spec.get();
QList<ContentAction::Action> actions =
ContentAction::Action::actionsForFile(QUrl(url), QString(mType.get()));
for (int i = 0; i < actions.size(); ++i) {
if (actions[i].name() == QString((QChar*)mName.get(), mName.Length())) {
actions[i].trigger();
break;
}
}
return NS_OK;
}

View file

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 __nsContentHandlerAppImpl_h__
#define __nsContentHandlerAppImpl_h__
#include <contentaction/contentaction.h>
#include "nsString.h"
#include "nsIMIMEInfo.h"
class nsContentHandlerApp : public nsIHandlerApp
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
nsContentHandlerApp(nsString aName, nsCString aType, ContentAction::Action& aAction);
virtual ~nsContentHandlerApp() { }
protected:
nsString mName;
nsCString mType;
nsString mDetailedDescription;
ContentAction::Action mAction;
};
#endif

View file

@ -0,0 +1,177 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 <dbus/dbus.h>
#include "mozilla/ipc/DBusConnectionDelete.h"
#include "mozilla/ipc/DBusMessageRefPtr.h"
#include "nsDBusHandlerApp.h"
#include "nsIURI.h"
#include "nsIClassInfoImpl.h"
#include "nsCOMPtr.h"
#include "nsCExternalHandlerService.h"
// XXX why does nsMIMEInfoImpl have a threadsafe nsISupports? do we need one
// here too?
NS_IMPL_CLASSINFO(nsDBusHandlerApp, nullptr, 0, NS_DBUSHANDLERAPP_CID)
NS_IMPL_ISUPPORTS_CI(nsDBusHandlerApp, nsIDBusHandlerApp, nsIHandlerApp)
////////////////////////////////////////////////////////////////////////////////
//// nsIHandlerApp
NS_IMETHODIMP nsDBusHandlerApp::GetName(nsAString& aName)
{
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::SetName(const nsAString & aName)
{
mName.Assign(aName);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::SetDetailedDescription(const nsAString & aDescription)
{
mDetailedDescription.Assign(aDescription);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::GetDetailedDescription(nsAString& aDescription)
{
aDescription.Assign(mDetailedDescription);
return NS_OK;
}
NS_IMETHODIMP
nsDBusHandlerApp::Equals(nsIHandlerApp *aHandlerApp, bool *_retval)
{
NS_ENSURE_ARG_POINTER(aHandlerApp);
// If the handler app isn't a dbus handler app, then it's not the same app.
nsCOMPtr<nsIDBusHandlerApp> dbusHandlerApp = do_QueryInterface(aHandlerApp);
if (!dbusHandlerApp) {
*_retval = false;
return NS_OK;
}
nsAutoCString service;
nsAutoCString method;
nsresult rv = dbusHandlerApp->GetService(service);
if (NS_FAILED(rv)) {
*_retval = false;
return NS_OK;
}
rv = dbusHandlerApp->GetMethod(method);
if (NS_FAILED(rv)) {
*_retval = false;
return NS_OK;
}
*_retval = service.Equals(mService) && method.Equals(mMethod);
return NS_OK;
}
NS_IMETHODIMP
nsDBusHandlerApp::LaunchWithURI(nsIURI *aURI,
nsIInterfaceRequestor *aWindowContext)
{
nsAutoCString spec;
nsresult rv = aURI->GetAsciiSpec(spec);
NS_ENSURE_SUCCESS(rv,rv);
const char* uri = spec.get();
DBusError err;
dbus_error_init(&err);
mozilla::UniquePtr<DBusConnection, mozilla::DBusConnectionDelete>
connection(dbus_bus_get_private(DBUS_BUS_SESSION, &err));
if (dbus_error_is_set(&err)) {
dbus_error_free(&err);
return NS_ERROR_FAILURE;
}
if (nullptr == connection) {
return NS_ERROR_FAILURE;
}
dbus_connection_set_exit_on_disconnect(connection.get(),false);
RefPtr<DBusMessage> msg = already_AddRefed<DBusMessage>(
dbus_message_new_method_call(mService.get(),
mObjpath.get(),
mInterface.get(),
mMethod.get()));
if (!msg) {
return NS_ERROR_FAILURE;
}
dbus_message_set_no_reply(msg, true);
DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uri);
if (dbus_connection_send(connection.get(), msg, nullptr)) {
dbus_connection_flush(connection.get());
} else {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// nsIDBusHandlerApp
NS_IMETHODIMP nsDBusHandlerApp::GetService(nsACString & aService)
{
aService.Assign(mService);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::SetService(const nsACString & aService)
{
mService.Assign(aService);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::GetMethod(nsACString & aMethod)
{
aMethod.Assign(mMethod);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::SetMethod(const nsACString & aMethod)
{
mMethod.Assign(aMethod);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::GetDBusInterface(nsACString & aInterface)
{
aInterface.Assign(mInterface);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::SetDBusInterface(const nsACString & aInterface)
{
mInterface.Assign(aInterface);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::GetObjectPath(nsACString & aObjpath)
{
aObjpath.Assign(mObjpath);
return NS_OK;
}
NS_IMETHODIMP nsDBusHandlerApp::SetObjectPath(const nsACString & aObjpath)
{
mObjpath.Assign(aObjpath);
return NS_OK;
}

View file

@ -0,0 +1,33 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 __nsDBusHandlerAppImpl_h__
#define __nsDBusHandlerAppImpl_h__
#include "nsString.h"
#include "nsIMIMEInfo.h"
class nsDBusHandlerApp : public nsIDBusHandlerApp
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
NS_DECL_NSIDBUSHANDLERAPP
nsDBusHandlerApp() { }
protected:
virtual ~nsDBusHandlerApp() { }
nsString mName;
nsString mDetailedDescription;
nsCString mService;
nsCString mMethod;
nsCString mInterface;
nsCString mObjpath;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,498 @@
/* -*- 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 nsExternalHelperAppService_h__
#define nsExternalHelperAppService_h__
#include "mozilla/Logging.h"
#include "prtime.h"
#include "nsIExternalHelperAppService.h"
#include "nsIExternalProtocolService.h"
#include "nsIWebProgressListener2.h"
#include "nsIHelperAppLauncherDialog.h"
#include "nsIMIMEInfo.h"
#include "nsIMIMEService.h"
#include "nsIStreamListener.h"
#include "nsIFile.h"
#include "nsIFileStreams.h"
#include "nsIOutputStream.h"
#include "nsString.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIChannel.h"
#include "nsITimer.h"
#include "nsIBackgroundFileSaver.h"
#include "nsIHandlerService.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsCOMArray.h"
#include "nsWeakReference.h"
#include "nsIPrompt.h"
#include "nsAutoPtr.h"
#include "mozilla/Attributes.h"
class nsExternalAppHandler;
class nsIMIMEInfo;
class nsITransfer;
class nsPIDOMWindowOuter;
/**
* The helper app service. Responsible for handling content that Mozilla
* itself can not handle
*/
class nsExternalHelperAppService
: public nsIExternalHelperAppService,
public nsPIExternalAppLauncher,
public nsIExternalProtocolService,
public nsIMIMEService,
public nsIObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEXTERNALHELPERAPPSERVICE
NS_DECL_NSPIEXTERNALAPPLAUNCHER
NS_DECL_NSIEXTERNALPROTOCOLSERVICE
NS_DECL_NSIMIMESERVICE
NS_DECL_NSIOBSERVER
nsExternalHelperAppService();
/**
* Initializes internal state. Will be called automatically when
* this service is first instantiated.
*/
MOZ_MUST_USE nsresult Init();
/**
* Given a mimetype and an extension, looks up a mime info from the OS.
* The mime type is given preference. This function follows the same rules
* as nsIMIMEService::GetFromTypeAndExtension.
* This is supposed to be overridden by the platform-specific
* nsOSHelperAppService!
* @param aFileExt The file extension; may be empty. UTF-8 encoded.
* @param [out] aFound
* Should be set to true if the os has a mapping, to
* false otherwise. Must not be null.
* @return A MIMEInfo. This function must return a MIMEInfo object if it
* can allocate one. The only justifiable reason for not
* returning one is an out-of-memory error.
* If null, the value of aFound is unspecified.
*/
virtual already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType,
const nsACString& aFileExt,
bool * aFound) = 0;
/**
* Given a string identifying an application, create an nsIFile representing
* it. This function should look in $PATH for the application.
* The base class implementation will first try to interpret platformAppPath
* as an absolute path, and if that fails it will look for a file next to the
* mozilla executable. Subclasses can override this method if they want a
* different behaviour.
* @param platformAppPath A platform specific path to an application that we
* got out of the rdf data source. This can be a mac
* file spec, a unix path or a windows path depending
* on the platform
* @param aFile [out] An nsIFile representation of that platform
* application path.
*/
virtual nsresult GetFileTokenForPath(const char16_t * platformAppPath,
nsIFile ** aFile);
virtual nsresult OSProtocolHandlerExists(const char *aScheme,
bool *aExists) = 0;
/**
* Given an extension, get a MIME type string. If not overridden by
* the OS-specific nsOSHelperAppService, will call into GetMIMEInfoFromOS
* with an empty mimetype.
* @return true if we successfully found a mimetype.
*/
virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension,
nsACString& aMIMEType);
protected:
virtual ~nsExternalHelperAppService();
/**
* Searches the "extra" array of MIMEInfo objects for an object
* with a specific type. If found, it will modify the passed-in
* MIMEInfo. Otherwise, it will return an error and the MIMEInfo
* will be untouched.
* @param aContentType The type to search for.
* @param aMIMEInfo [inout] The mime info, if found
*/
nsresult FillMIMEInfoForMimeTypeFromExtras(
const nsACString& aContentType, nsIMIMEInfo * aMIMEInfo);
/**
* Searches the "extra" array of MIMEInfo objects for an object
* with a specific extension.
*
* Does not change the MIME Type of the MIME Info.
*
* @see FillMIMEInfoForMimeTypeFromExtras
*/
nsresult FillMIMEInfoForExtensionFromExtras(
const nsACString& aExtension, nsIMIMEInfo * aMIMEInfo);
/**
* Searches the "extra" array for a MIME type, and gets its extension.
* @param aExtension The extension to search for
* @param aMIMEType [out] The found MIME type.
* @return true if the extension was found, false otherwise.
*/
bool GetTypeFromExtras(const nsACString& aExtension,
nsACString& aMIMEType);
/**
* Logging Module. Usage: set MOZ_LOG=HelperAppService:level, where level
* should be 2 for errors, 3 for debug messages from the cross- platform
* nsExternalHelperAppService, and 4 for os-specific debug messages.
*/
static mozilla::LazyLogModule mLog;
// friend, so that it can access the nspr log module.
friend class nsExternalAppHandler;
/**
* Helper function for ExpungeTemporaryFiles and ExpungeTemporaryPrivateFiles
*/
static void ExpungeTemporaryFilesHelper(nsCOMArray<nsIFile> &fileList);
/**
* Helper function for DeleteTemporaryFileOnExit and DeleteTemporaryPrivateFileWhenPossible
*/
static nsresult DeleteTemporaryFileHelper(nsIFile* aTemporaryFile,
nsCOMArray<nsIFile> &aFileList);
/**
* Functions related to the tempory file cleanup service provided by
* nsExternalHelperAppService
*/
void ExpungeTemporaryFiles();
/**
* Functions related to the tempory file cleanup service provided by
* nsExternalHelperAppService (for the temporary files added during
* the private browsing mode)
*/
void ExpungeTemporaryPrivateFiles();
/**
* Array for the files that should be deleted
*/
nsCOMArray<nsIFile> mTemporaryFilesList;
/**
* Array for the files that should be deleted (for the temporary files
* added during the private browsing mode)
*/
nsCOMArray<nsIFile> mTemporaryPrivateFilesList;
private:
nsresult DoContentContentProcessHelper(const nsACString& aMimeContentType,
nsIRequest *aRequest,
nsIInterfaceRequestor *aContentContext,
bool aForceSave,
nsIInterfaceRequestor *aWindowContext,
nsIStreamListener ** aStreamListener);
};
/**
* An external app handler is just a small little class that presents itself as
* a nsIStreamListener. It saves the incoming data into a temp file. The handler
* is bound to an application when it is created. When it receives an
* OnStopRequest it launches the application using the temp file it has
* stored the data into. We create a handler every time we have to process
* data using a helper app.
*/
class nsExternalAppHandler final : public nsIStreamListener,
public nsIHelperAppLauncher,
public nsITimerCallback,
public nsIBackgroundFileSaverObserver
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSIHELPERAPPLAUNCHER
NS_DECL_NSICANCELABLE
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIBACKGROUNDFILESAVEROBSERVER
/**
* @param aMIMEInfo MIMEInfo object, representing the type of the
* content that should be handled
* @param aFileExtension The extension we need to append to our temp file,
* INCLUDING the ".". e.g. .mp3
* @param aContentContext dom Window context, as passed to DoContent.
* @param aWindowContext Top level window context used in dialog parenting,
* as passed to DoContent. This parameter may be null,
* in which case dialogs will be parented to
* aContentContext.
* @param mExtProtSvc nsExternalHelperAppService on creation
* @param aFileName The filename to use
* @param aReason A constant from nsIHelperAppLauncherDialog indicating
* why the request is handled by a helper app.
*/
nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsCSubstring& aFileExtension,
nsIInterfaceRequestor * aContentContext,
nsIInterfaceRequestor * aWindowContext,
nsExternalHelperAppService * aExtProtSvc,
const nsAString& aFilename,
uint32_t aReason, bool aForceSave);
/**
* Clean up after the request was diverted to the parent process.
*/
void DidDivertRequest(nsIRequest *request);
/**
* Apply content conversions if needed.
*/
void MaybeApplyDecodingForExtension(nsIRequest *request);
protected:
~nsExternalAppHandler();
nsIInterfaceRequestor* GetDialogParent() {
return mWindowContext ? mWindowContext : mContentContext;
}
nsCOMPtr<nsIFile> mTempFile;
nsCOMPtr<nsIURI> mSourceUrl;
nsString mTempFileExtension;
nsString mTempLeafName;
/**
* The MIME Info for this load. Will never be null.
*/
nsCOMPtr<nsIMIMEInfo> mMimeInfo;
/**
* The dom window associated with this request to handle content.
*/
nsCOMPtr<nsIInterfaceRequestor> mContentContext;
/**
* If set, the parent window helper app dialogs and file pickers
* should use in parenting. If null, we use mContentContext.
*/
nsCOMPtr<nsIInterfaceRequestor> mWindowContext;
/**
* Used to close the window on a timer, to avoid any exceptions that are
* thrown if we try to close the window before it's fully loaded.
*/
nsCOMPtr<nsPIDOMWindowOuter> mWindowToClose;
nsCOMPtr<nsITimer> mTimer;
/**
* The following field is set if we were processing an http channel that had
* a content disposition header which specified the SUGGESTED file name we
* should present to the user in the save to disk dialog.
*/
nsString mSuggestedFileName;
/**
* If set, this handler should forcibly save the file to disk regardless of
* MIME info settings or anything else, without ever popping up the
* unknown content type handling dialog.
*/
bool mForceSave;
/**
* The canceled flag is set if the user canceled the launching of this
* application before we finished saving the data to a temp file.
*/
bool mCanceled;
/**
* This is set based on whether the channel indicates that a new window
* was opened specifically for this download. If so, then we
* close it.
*/
bool mShouldCloseWindow;
/**
* True if a stop request has been issued.
*/
bool mStopRequestIssued;
bool mIsFileChannel;
/**
* One of the REASON_ constants from nsIHelperAppLauncherDialog. Indicates the
* reason the dialog was shown (unknown content type, server requested it,
* etc).
*/
uint32_t mReason;
/**
* Track the executable-ness of the temporary file.
*/
bool mTempFileIsExecutable;
PRTime mTimeDownloadStarted;
int64_t mContentLength;
int64_t mProgress; /**< Number of bytes received (for sending progress notifications). */
/**
* When we are told to save the temp file to disk (in a more permament
* location) before we are done writing the content to a temp file, then
* we need to remember the final destination until we are ready to use it.
*/
nsCOMPtr<nsIFile> mFinalFileDestination;
uint32_t mBufferSize;
/**
* This object handles saving the data received from the network to a
* temporary location first, and then move the file to its final location,
* doing all the input/output on a background thread.
*/
nsCOMPtr<nsIBackgroundFileSaver> mSaver;
/**
* Stores the SHA-256 hash associated with the file that we downloaded.
*/
nsAutoCString mHash;
/**
* Stores the signature information of the downloaded file in an nsIArray of
* nsIX509CertList of nsIX509Cert. If the file is unsigned this will be
* empty.
*/
nsCOMPtr<nsIArray> mSignatureInfo;
/**
* Stores the redirect information associated with the channel.
*/
nsCOMPtr<nsIArray> mRedirects;
/**
* Creates the temporary file for the download and an output stream for it.
* Upon successful return, both mTempFile and mSaver will be valid.
*/
nsresult SetUpTempFile(nsIChannel * aChannel);
/**
* When we download a helper app, we are going to retarget all load
* notifications into our own docloader and load group instead of
* using the window which initiated the load....RetargetLoadNotifications
* contains that information...
*/
void RetargetLoadNotifications(nsIRequest *request);
/**
* Once the user tells us how they want to dispose of the content
* create an nsITransfer so they know what's going on. If this fails, the
* caller MUST call Cancel.
*/
nsresult CreateTransfer();
/**
* If we fail to create the necessary temporary file to initiate a transfer
* we will report the failure by creating a failed nsITransfer.
*/
nsresult CreateFailedTransfer(bool aIsPrivateBrowsing);
/*
* The following two functions are part of the split of SaveToDisk
* to make it async, and works as following:
*
* SaveToDisk -------> RequestSaveDestination
* .
* .
* v
* ContinueSave <------- SaveDestinationAvailable
*/
/**
* This is called by SaveToDisk to decide what's the final
* file destination chosen by the user or by auto-download settings.
*/
void RequestSaveDestination(const nsAFlatString &aDefaultFile,
const nsAFlatString &aDefaultFileExt);
/**
* When SaveToDisk is called, it possibly delegates to RequestSaveDestination
* to decide the file destination. ContinueSave must then be called when
* the final destination is finally known.
* @param aFile The file that was chosen as the final destination.
* Must not be null.
*/
nsresult ContinueSave(nsIFile* aFile);
/**
* After we're done prompting the user for any information, if the original
* channel had a refresh url associated with it (which might point to a
* "thank you for downloading" kind of page, then process that....It is safe
* to invoke this method multiple times. We'll clear mOriginalChannel after
* it's called and this ensures we won't call it again....
*/
void ProcessAnyRefreshTags();
/**
* Notify our nsITransfer object that we are done with the download. This is
* always called after the target file has been closed.
*
* @param aStatus
* NS_OK for success, or a failure code if the download failed.
* A partially downloaded file may still be available in this case.
*/
void NotifyTransfer(nsresult aStatus);
/**
* Helper routine that searches a pref string for a given mime type
*/
bool GetNeverAskFlagFromPref(const char * prefName, const char * aContentType);
/**
* Helper routine to ensure mSuggestedFileName is "correct";
* this ensures that mTempFileExtension only contains an extension when it
* is different from mSuggestedFileName's extension.
*/
void EnsureSuggestedFileName();
typedef enum { kReadError, kWriteError, kLaunchError } ErrorType;
/**
* Utility function to send proper error notification to web progress listener
*/
void SendStatusChange(ErrorType type, nsresult aStatus, nsIRequest *aRequest, const nsAFlatString &path);
/**
* Closes the window context if it does not have a refresh header
* and it never displayed content before the external helper app
* service was invoked.
*/
nsresult MaybeCloseWindow();
/**
* Set in nsHelperDlgApp.js. This is always null after the user has chosen an
* action.
*/
nsCOMPtr<nsIWebProgressListener2> mDialogProgressListener;
/**
* Set once the user has chosen an action. This is null after the download
* has been canceled or completes.
*/
nsCOMPtr<nsITransfer> mTransfer;
nsCOMPtr<nsIChannel> mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */
nsCOMPtr<nsIHelperAppLauncherDialog> mDialog;
/**
* Keep request alive in case when helper non-modal dialog shown.
* Thus in OnStopRequest the mRequest will not be set to null (it will be set to null further).
*/
bool mKeepRequestAlive;
/**
* The request that's being loaded. Initialized in OnStartRequest.
* Nulled out in OnStopRequest or once we know what we're doing
* with the data, whichever happens later.
*/
nsCOMPtr<nsIRequest> mRequest;
RefPtr<nsExternalHelperAppService> mExtProtSvc;
};
#endif // nsExternalHelperAppService_h__

View file

@ -0,0 +1,561 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:set ts=2 sts=2 sw=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 "nsIURI.h"
#include "nsIURL.h"
#include "nsExternalProtocolHandler.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsIServiceManager.h"
#include "nsServiceManagerUtils.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIStringBundle.h"
#include "nsIPrefService.h"
#include "nsIPrompt.h"
#include "nsNetUtil.h"
#include "nsContentSecurityManager.h"
#include "nsExternalHelperAppService.h"
// used to dispatch urls to default protocol handlers
#include "nsCExternalHandlerService.h"
#include "nsIExternalProtocolService.h"
#include "nsIChildChannel.h"
#include "nsIParentChannel.h"
class nsILoadInfo;
////////////////////////////////////////////////////////////////////////
// a stub channel implemenation which will map calls to AsyncRead and OpenInputStream
// to calls in the OS for loading the url.
////////////////////////////////////////////////////////////////////////
class nsExtProtocolChannel : public nsIChannel,
public nsIChildChannel,
public nsIParentChannel
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICHANNEL
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUEST
NS_DECL_NSICHILDCHANNEL
NS_DECL_NSIPARENTCHANNEL
nsExtProtocolChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo);
private:
virtual ~nsExtProtocolChannel();
nsresult OpenURL();
void Finish(nsresult aResult);
nsCOMPtr<nsIURI> mUrl;
nsCOMPtr<nsIURI> mOriginalURI;
nsresult mStatus;
nsLoadFlags mLoadFlags;
bool mWasOpened;
// Set true (as a result of ConnectParent invoked from child process)
// when this channel is on the parent process and is being used as
// a redirect target channel. It turns AsyncOpen into a no-op since
// we do it on the child.
bool mConnectedParent;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsILoadInfo> mLoadInfo;
};
NS_IMPL_ADDREF(nsExtProtocolChannel)
NS_IMPL_RELEASE(nsExtProtocolChannel)
NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
NS_INTERFACE_MAP_ENTRY(nsIChannel)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_END_THREADSAFE
nsExtProtocolChannel::nsExtProtocolChannel(nsIURI* aURI,
nsILoadInfo* aLoadInfo)
: mUrl(aURI)
, mOriginalURI(aURI)
, mStatus(NS_OK)
, mLoadFlags(nsIRequest::LOAD_NORMAL)
, mWasOpened(false)
, mConnectedParent(false)
, mLoadInfo(aLoadInfo)
{
}
nsExtProtocolChannel::~nsExtProtocolChannel()
{}
NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup)
{
NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup * aLoadGroup)
{
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
{
NS_IF_ADDREF(*aCallbacks = mCallbacks);
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
{
mCallbacks = aCallbacks;
return NS_OK;
}
NS_IMETHODIMP
nsExtProtocolChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nullptr;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::GetOriginalURI(nsIURI* *aURI)
{
NS_ADDREF(*aURI = mOriginalURI);
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::SetOriginalURI(nsIURI* aURI)
{
NS_ENSURE_ARG_POINTER(aURI);
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::GetURI(nsIURI* *aURI)
{
*aURI = mUrl;
NS_IF_ADDREF(*aURI);
return NS_OK;
}
nsresult nsExtProtocolChannel::OpenURL()
{
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIExternalProtocolService> extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
if (extProtService)
{
#ifdef DEBUG
nsAutoCString urlScheme;
mUrl->GetScheme(urlScheme);
bool haveHandler = false;
extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler);
NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?");
#endif
nsCOMPtr<nsIInterfaceRequestor> aggCallbacks;
rv = NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
getter_AddRefs(aggCallbacks));
if (NS_FAILED(rv)) {
goto finish;
}
rv = extProtService->LoadURI(mUrl, aggCallbacks);
if (NS_SUCCEEDED(rv)) {
// despite success, we need to abort this channel, at the very least
// to make it clear to the caller that no on{Start,Stop}Request
// should be expected.
rv = NS_ERROR_NO_CONTENT;
}
}
finish:
mCallbacks = nullptr;
return rv;
}
NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream **_retval)
{
return OpenURL();
}
NS_IMETHODIMP nsExtProtocolChannel::Open2(nsIInputStream** aStream)
{
nsCOMPtr<nsIStreamListener> listener;
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
return Open(aStream);
}
NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
{
if (mConnectedParent) {
return NS_OK;
}
MOZ_ASSERT(!mLoadInfo ||
mLoadInfo->GetSecurityMode() == 0 ||
mLoadInfo->GetInitialSecurityCheckDone() ||
(mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
"security flags in loadInfo but asyncOpen2() not called");
NS_ENSURE_ARG_POINTER(listener);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
mWasOpened = true;
return OpenURL();
}
NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen2(nsIStreamListener *aListener)
{
nsCOMPtr<nsIStreamListener> listener = aListener;
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
return AsyncOpen(listener, nullptr);
}
NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
{
*aLoadFlags = mLoadFlags;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
{
mLoadFlags = aLoadFlags;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString &aContentType)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExtProtocolChannel::SetContentType(const nsACString &aContentType)
{
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsExtProtocolChannel::GetContentCharset(nsACString &aContentCharset)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(const nsACString &aContentCharset)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(uint32_t *aContentDisposition)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP nsExtProtocolChannel::SetContentDisposition(uint32_t aContentDisposition)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP nsExtProtocolChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(int64_t * aContentLength)
{
*aContentLength = -1;
return NS_OK;
}
NS_IMETHODIMP
nsExtProtocolChannel::SetContentLength(int64_t aContentLength)
{
NS_NOTREACHED("SetContentLength");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExtProtocolChannel::GetOwner(nsISupports * *aPrincipal)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExtProtocolChannel::SetOwner(nsISupports * aPrincipal)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExtProtocolChannel::GetLoadInfo(nsILoadInfo **aLoadInfo)
{
NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::SetLoadInfo(nsILoadInfo *aLoadInfo)
{
mLoadInfo = aLoadInfo;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// From nsIRequest
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP nsExtProtocolChannel::GetName(nsACString &result)
{
return mUrl->GetSpec(result);
}
NS_IMETHODIMP nsExtProtocolChannel::IsPending(bool *result)
{
*result = false;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::GetStatus(nsresult *status)
{
*status = mStatus;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::Cancel(nsresult status)
{
mStatus = status;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::Suspend()
{
NS_NOTREACHED("Suspend");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExtProtocolChannel::Resume()
{
NS_NOTREACHED("Resume");
return NS_ERROR_NOT_IMPLEMENTED;
}
///////////////////////////////////////////////////////////////////////
// From nsIChildChannel
//////////////////////////////////////////////////////////////////////
NS_IMETHODIMP nsExtProtocolChannel::ConnectParent(uint32_t registrarId)
{
mozilla::dom::ContentChild::GetSingleton()->
SendExtProtocolChannelConnectParent(registrarId);
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::CompleteRedirectSetup(nsIStreamListener *listener,
nsISupports *context)
{
// For redirects to external protocols we AsyncOpen on the child
// (not the parent) because child channel has the right docshell
// (which is needed for the select dialog).
return AsyncOpen(listener, context);
}
///////////////////////////////////////////////////////////////////////
// From nsIParentChannel (derives from nsIStreamListener)
//////////////////////////////////////////////////////////////////////
NS_IMETHODIMP nsExtProtocolChannel::SetParentListener(HttpChannelParentListener* aListener)
{
// This is called as part of the connect parent operation from
// ContentParent::RecvExtProtocolChannelConnectParent. Setting
// this flag tells this channel to not proceed and makes AsyncOpen
// just no-op. Actual operation will happen from the child process
// via CompleteRedirectSetup call on the child channel.
mConnectedParent = true;
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingProtectionDisabled()
{
// nothing to do
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::Delete()
{
// nothing to do
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::OnStartRequest(nsIRequest *aRequest,
nsISupports *aContext)
{
// no data is expected
MOZ_CRASH("No data expected from external protocol channel");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP nsExtProtocolChannel::OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode)
{
// no data is expected
MOZ_CRASH("No data expected from external protocol channel");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP nsExtProtocolChannel::OnDataAvailable(nsIRequest *aRequest,
nsISupports *aContext,
nsIInputStream *aInputStream,
uint64_t aOffset,
uint32_t aCount)
{
// no data is expected
MOZ_CRASH("No data expected from external protocol channel");
return NS_ERROR_UNEXPECTED;
}
///////////////////////////////////////////////////////////////////////
// the default protocol handler implementation
//////////////////////////////////////////////////////////////////////
nsExternalProtocolHandler::nsExternalProtocolHandler()
{
m_schemeName = "default";
}
nsExternalProtocolHandler::~nsExternalProtocolHandler()
{}
NS_IMPL_ADDREF(nsExternalProtocolHandler)
NS_IMPL_RELEASE(nsExternalProtocolHandler)
NS_INTERFACE_MAP_BEGIN(nsExternalProtocolHandler)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolHandler)
NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler)
NS_INTERFACE_MAP_ENTRY(nsIExternalProtocolHandler)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_THREADSAFE
NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString &aScheme)
{
aScheme = m_schemeName;
return NS_OK;
}
NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(int32_t *aDefaultPort)
{
*aDefaultPort = 0;
return NS_OK;
}
NS_IMETHODIMP
nsExternalProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
{
// don't override anything.
*_retval = false;
return NS_OK;
}
// returns TRUE if the OS can handle this protocol scheme and false otherwise.
bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI * aURI)
{
MOZ_ASSERT(aURI);
nsAutoCString scheme;
aURI->GetScheme(scheme);
nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
if (!extProtSvc) {
return false;
}
bool haveHandler = false;
extProtSvc->ExternalProtocolHandlerExists(scheme.get(), &haveHandler);
return haveHandler;
}
NS_IMETHODIMP nsExternalProtocolHandler::GetProtocolFlags(uint32_t *aUritype)
{
// Make it norelative since it is a simple uri
*aUritype = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE |
URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA;
return NS_OK;
}
NS_IMETHODIMP nsExternalProtocolHandler::NewURI(const nsACString &aSpec,
const char *aCharset, // ignore charset info
nsIURI *aBaseURI,
nsIURI **_retval)
{
nsresult rv;
nsCOMPtr<nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = uri->SetSpec(aSpec);
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*_retval = uri);
return NS_OK;
}
NS_IMETHODIMP
nsExternalProtocolHandler::NewChannel2(nsIURI* aURI,
nsILoadInfo* aLoadInfo,
nsIChannel** aRetval)
{
NS_ENSURE_TRUE(aURI, NS_ERROR_UNKNOWN_PROTOCOL);
NS_ENSURE_TRUE(aRetval, NS_ERROR_UNKNOWN_PROTOCOL);
// Only try to return a channel if we have a protocol handler for the url.
// nsOSHelperAppService::LoadUriInternal relies on this to check trustedness
// for some platforms at least. (win uses ::ShellExecute and unix uses
// gnome_url_show.)
if (!HaveExternalProtocolHandler(aURI)) {
return NS_ERROR_UNKNOWN_PROTOCOL;
}
nsCOMPtr<nsIChannel> channel = new nsExtProtocolChannel(aURI, aLoadInfo);
channel.forget(aRetval);
return NS_OK;
}
NS_IMETHODIMP nsExternalProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
{
return NewChannel2(aURI, nullptr, _retval);
}
///////////////////////////////////////////////////////////////////////
// External protocol handler interface implementation
//////////////////////////////////////////////////////////////////////
NS_IMETHODIMP nsExternalProtocolHandler::ExternalAppExistsForScheme(const nsACString& aScheme, bool *_retval)
{
nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
if (extProtSvc)
return extProtSvc->ExternalProtocolHandlerExists(
PromiseFlatCString(aScheme).get(), _retval);
// In case we don't have external protocol service.
*_retval = false;
return NS_OK;
}

View file

@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 4; 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 nsExternalProtocolHandler_h___
#define nsExternalProtocolHandler_h___
#include "nsIExternalProtocolHandler.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsWeakReference.h"
#include "nsIExternalProtocolService.h"
#include "mozilla/Attributes.h"
class nsIURI;
// protocol handlers need to support weak references if we want the netlib nsIOService to cache them.
class nsExternalProtocolHandler final : public nsIExternalProtocolHandler, public nsSupportsWeakReference
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
NS_DECL_NSIEXTERNALPROTOCOLHANDLER
nsExternalProtocolHandler();
protected:
~nsExternalProtocolHandler();
// helper function
bool HaveExternalProtocolHandler(nsIURI * aURI);
nsCString m_schemeName;
};
#endif // nsExternalProtocolHandler_h___

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
component {32314cc8-22f7-4f7f-a645-1a45453ba6a6} nsHandlerService.js
contract @mozilla.org/uriloader/handler-service;1 {32314cc8-22f7-4f7f-a645-1a45453ba6a6} process=main

View file

@ -0,0 +1,45 @@
/* 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 "nsISupports.idl"
interface nsIHandlerInfo;
interface nsIHelperAppLauncher;
interface nsIURI;
interface nsIInterfaceRequestor;
/**
* This is used to ask a user what they would like to do with a given piece of
* content.
*/
[scriptable, uuid(456ca3b2-02be-4f97-89a2-08c08d3ad88f)]
interface nsIContentDispatchChooser : nsISupports {
/**
* This request is passed to the helper app dialog because Gecko can not
* handle content of this type.
*/
const unsigned long REASON_CANNOT_HANDLE = 0;
/**
* Asks the user what to do with the content.
*
* @param aHander
* The interface describing the details of how this content should or
* can be handled.
* @param aWindowContext
* The parent window context to show this chooser. This can be null,
* and some implementations may not care about it. Generally, you'll
* want to pass an nsIDOMWindow in so the chooser can be properly
* parented when opened.
* @param aURI
* The URI of the resource that we are asking about.
* @param aReason
* The reason why we are asking (see above).
*/
void ask(in nsIHandlerInfo aHandler,
in nsIInterfaceRequestor aWindowContext,
in nsIURI aURI,
in unsigned long aReason);
};

View file

@ -0,0 +1,154 @@
/* -*- Mode: C++; tab-width: 3; 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 "nsICancelable.idl"
interface nsIURI;
interface nsIRequest;
interface nsIStreamListener;
interface nsIFile;
interface nsIMIMEInfo;
interface nsIWebProgressListener2;
interface nsIInterfaceRequestor;
/**
* The external helper app service is used for finding and launching
* platform specific external applications for a given mime content type.
*/
[scriptable, uuid(1E4F3AE1-B737-431F-A95D-31FA8DA70199)]
interface nsIExternalHelperAppService : nsISupports
{
/**
* Binds an external helper application to a stream listener. The caller
* should pump data into the returned stream listener. When the OnStopRequest
* is issued, the stream listener implementation will launch the helper app
* with this data.
* @param aMimeContentType The content type of the incoming data
* @param aRequest The request corresponding to the incoming data
* @param aContentContext Used in processing content document refresh
* headers after target content is downloaded. Note in e10s land
* this is likely a CPOW that points to a window in the child process.
* @param aForceSave True to always save this content to disk, regardless of
* nsIMIMEInfo and other such influences.
* @param aWindowContext Used in parenting helper app dialogs, usually
* points to the parent browser window. This parameter may be null,
* in which case dialogs will be parented to aContentContext.
* @return A nsIStreamListener which the caller should pump the data into.
*/
nsIStreamListener doContent (in ACString aMimeContentType,
in nsIRequest aRequest,
in nsIInterfaceRequestor aContentContext,
in boolean aForceSave,
[optional] in nsIInterfaceRequestor aWindowContext);
/**
* Returns true if data from a URL with this extension combination
* is to be decoded from aEncodingType prior to saving or passing
* off to helper apps, false otherwise.
*/
boolean applyDecodingForExtension(in AUTF8String aExtension,
in ACString aEncodingType);
};
/**
* This is a private interface shared between external app handlers and the platform specific
* external helper app service
*/
[scriptable, uuid(6613e2e7-feab-4e3a-bb1f-b03200d544ec)]
interface nsPIExternalAppLauncher : nsISupports
{
/**
* mscott --> eventually I should move this into a new service so other
* consumers can add temporary files they want deleted on exit.
* @param aTemporaryFile A temporary file we should delete on exit.
*/
void deleteTemporaryFileOnExit(in nsIFile aTemporaryFile);
/**
* Delete a temporary file created inside private browsing mode when
* the private browsing mode has ended.
*/
void deleteTemporaryPrivateFileWhenPossible(in nsIFile aTemporaryFile);
};
/**
* A helper app launcher is a small object created to handle the launching
* of an external application.
*
* Note that cancelling the load via the nsICancelable interface will release
* the reference to the launcher dialog.
*/
[scriptable, uuid(acf2a516-7d7f-4771-8b22-6c4a559c088e)]
interface nsIHelperAppLauncher : nsICancelable
{
/**
* The mime info object associated with the content type this helper app
* launcher is currently attempting to load
*/
readonly attribute nsIMIMEInfo MIMEInfo;
/**
* The source uri
*/
readonly attribute nsIURI source;
/**
* The suggested name for this file
*/
readonly attribute AString suggestedFileName;
/**
* Saves the final destination of the file. Does not actually perform the
* save.
* NOTE: This will release the reference to the
* nsIHelperAppLauncherDialog.
*/
void saveToDisk(in nsIFile aNewFileLocation, in boolean aRememberThisPreference);
/**
* Remembers that aApplication should be used to launch this content. Does
* not actually launch the application.
* NOTE: This will release the reference to the nsIHelperAppLauncherDialog.
* @param aApplication nsIFile corresponding to the location of the application to use.
* @param aRememberThisPreference TRUE if we should remember this choice.
*/
void launchWithApplication(in nsIFile aApplication, in boolean aRememberThisPreference);
/**
* Callback invoked by nsIHelperAppLauncherDialog::promptForSaveToFileAsync
* after the user has chosen a file through the File Picker (or dismissed it).
* @param aFile The file that was chosen by the user (or null if dialog was dismissed).
*/
void saveDestinationAvailable(in nsIFile aFile);
/**
* The following methods are used by the progress dialog to get or set
* information on the current helper app launcher download.
* This reference will be released when the download is finished (after the
* listener receives the STATE_STOP notification).
*/
void setWebProgressListener(in nsIWebProgressListener2 aWebProgressListener);
/**
* The file we are saving to
*/
readonly attribute nsIFile targetFile;
/**
* The executable-ness of the target file
*/
readonly attribute boolean targetFileIsExecutable;
/**
* Time when the download started
*/
readonly attribute PRTime timeDownloadStarted;
/**
* The download content length, or -1 if the length is not available.
*/
readonly attribute int64_t contentLength;
};

View file

@ -0,0 +1,135 @@
/* -*- Mode: C++; tab-width: 3; 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 "nsISupports.idl"
interface nsIURI;
interface nsIFile;
interface nsIInterfaceRequestor;
interface nsIHandlerInfo;
/**
* The external protocol service is used for finding and launching
* web handlers (a la registerProtocolHandler in the HTML5 draft) or
* platform-specific applications for handling particular protocols.
*
* You can ask the external protocol service if it has an external
* handler for a given protocol scheme. And you can ask it to load
* the url using the default handler.
*/
[scriptable, uuid(70f93b7a-3ec6-4bcb-b093-92d9984c9f83)]
interface nsIExternalProtocolService : nsISupports
{
/**
* Check whether a handler for a specific protocol exists. Specifically,
* this looks to see whether there are any known possible application handlers
* in either the nsIHandlerService datastore or registered with the OS.
*
* @param aProtocolScheme The scheme from a url: http, ftp, mailto, etc.
*
* @return true if we have a handler and false otherwise.
*
* XXX shouldn't aProtocolScheme be an ACString like nsIURI::scheme?
*/
boolean externalProtocolHandlerExists(in string aProtocolScheme);
/**
* Check whether a handler for a specific protocol is "exposed" as a visible
* feature of the current application.
*
* An exposed protocol handler is one that can be used in all contexts. A
* non-exposed protocol handler is one that can only be used internally by the
* application. For example, a non-exposed protocol would not be loaded by the
* application in response to a link click or a X-remote openURL command.
* Instead, it would be deferred to the system's external protocol handler.
* XXX shouldn't aProtocolScheme be an ACString like nsIURI::scheme?
*/
boolean isExposedProtocol(in string aProtocolScheme);
/**
* Retrieve the handler for the given protocol. If neither the application
* nor the OS knows about a handler for the protocol, the object this method
* returns will represent a default handler for unknown content.
*
* @param aProtocolScheme the scheme from a URL: http, ftp, mailto, etc.
*
* Note: aProtocolScheme should not include a trailing colon, which is part
* of the URI syntax, not part of the scheme itself (i.e. pass "mailto" not
* "mailto:").
*
* @return the handler, if any; otherwise a default handler
*/
nsIHandlerInfo getProtocolHandlerInfo(in ACString aProtocolScheme);
/**
* Given a scheme, looks up the protocol info from the OS. This should be
* overridden by each OS's implementation.
*
* @param aScheme The protocol scheme we are looking for.
* @param aFound Was an OS default handler for this scheme found?
* @return An nsIHanderInfo for the protocol.
*/
nsIHandlerInfo getProtocolHandlerInfoFromOS(in ACString aProtocolScheme,
out boolean aFound);
/**
* Set some sane defaults for a protocol handler object.
*
* @param aHandlerInfo nsIHandlerInfo object, as returned by
* getProtocolHandlerInfoFromOS
* @param aOSHandlerExists was the object above created for an extant
* OS default handler? This is generally the
* value of the aFound out param from
* getProtocolHandlerInfoFromOS.
*/
void setProtocolHandlerDefaults(in nsIHandlerInfo aHandlerInfo,
in boolean aOSHandlerExists);
/**
* Used to load a url via an external protocol handler (if one exists)
*
* @param aURL The url to load
*
* @deprecated Use LoadURI instead (See Bug 389565 for removal)
*/
[deprecated] void loadUrl(in nsIURI aURL);
/**
* Used to load a URI via an external application. Might prompt the user for
* permission to load the external application.
*
* @param aURI
* The URI to load
*
* @param aWindowContext
* The window to parent the dialog against, and, if a web handler
* is chosen, it is loaded in this window as well. This parameter
* may be ultimately passed nsIURILoader.openURI in the case of a
* web handler, and aWindowContext is null or not present, web
* handlers will fail. We need to do better than that; bug 394483
* filed in order to track.
*
* @note Embedders that do not expose the http protocol should not currently
* use web-based protocol handlers, as handoff won't work correctly
* (bug 394479).
*/
void loadURI(in nsIURI aURI,
[optional] in nsIInterfaceRequestor aWindowContext);
/**
* Gets a human-readable description for the application responsible for
* handling a specific protocol.
*
* @param aScheme The scheme to look up. For example, "mms".
*
* @throw NS_ERROR_NOT_IMPLEMENTED
* If getting descriptions for protocol helpers is not supported
* @throw NS_ERROR_NOT_AVAILABLE
* If no protocol helper exists for this scheme, or if it is not
* possible to get a description for it.
*/
AString getApplicationDescription(in AUTF8String aScheme);
};

View file

@ -0,0 +1,28 @@
/* 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 "nsIMIMEInfo.idl"
%{C++
#define NS_EXTERNALSHARINGAPPSERVICE_CONTRACTID "@mozilla.org/uriloader/external-sharing-app-service;1"
%}
[scriptable, uuid(7111f769-53ec-41fd-b314-613661d5b6ba)]
interface nsISharingHandlerApp : nsIHandlerApp
{
void share(in AString data, [optional] in AString title);
};
[scriptable, uuid(cf7d04e5-3892-482e-81bb-073dc1c83f76)]
interface nsIExternalSharingAppService : nsISupports {
void shareWithDefault(in AString data, in AString mime,
[optional] in AString title);
void getSharingApps(in AString aMIMEType,
[optional] out unsigned long aLen,
[array, size_is(aLen), retval] out nsISharingHandlerApp handlerApps);
};

View file

@ -0,0 +1,26 @@
/* 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 "nsIMIMEInfo.idl"
/**
* The external URL handler service is used for finding
* platform-specific applications for handling particular URLs.
*/
[scriptable, uuid(56c5c7d3-6fd3-43f8-9429-4397e111453a)]
interface nsIExternalURLHandlerService : nsISupports
{
/**
* Given a URL, looks up the handler info from the OS. This should be
* overridden by each OS's implementation.
*
* @param aURL The URL we are looking for.
* @param aFound Was an OS default handler for this URL found?
* @return An nsIHanderInfo for the protocol.
*/
nsIHandlerInfo getURLHandlerInfoFromOS(in nsIURI aURL,
out boolean aFound);
};

View file

@ -0,0 +1,117 @@
/* 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 "nsISupports.idl"
interface nsIHandlerInfo;
interface nsISimpleEnumerator;
[scriptable, uuid(53f0ad17-ec62-46a1-adbc-efccc06babcd)]
interface nsIHandlerService : nsISupports
{
/**
* Retrieve a list of all handlers in the datastore. This list is not
* guaranteed to be in any particular order, and callers should not assume
* it will remain in the same order in the future.
*
* @returns a list of all handlers in the datastore
*/
nsISimpleEnumerator enumerate();
/**
* Fill a handler info object with information from the datastore.
*
* Note: because of the way the external helper app service currently mixes
* OS and user handler info in the same handler info object, this method
* takes an existing handler info object (probably retrieved from the OS)
* and "fills it in" with information from the datastore, overriding any
* existing properties on the object with properties from the datastore.
*
* Ultimately, however, we're going to separate OS and user handler info
* into separate objects, at which point this method should be renamed to
* something like "get" or "retrieve", take a class and type (or perhaps
* a type whose class can be determined by querying the type, for example
* an nsIContentType which is also an nsIMIMEType or an nsIProtocolScheme),
* and return a handler info object representing only the user info.
*
* Note: if you specify an override type, then the service will fill in
* the handler info object with information about that type instead of
* the type specified by the object's nsIHandlerInfo::type attribute.
*
* This is useful when you are trying to retrieve information about a MIME
* type that doesn't exist in the datastore, but you have a file extension
* for that type, and nsIHandlerService::getTypeFromExtension returns another
* MIME type that does exist in the datastore and can handle that extension.
*
* For example, the user clicks on a link, and the content has a MIME type
* that isn't in the datastore, but the link has a file extension, and that
* extension is associated with another MIME type in the datastore (perhaps
* an unofficial MIME type preceded an official one, like with image/x-png
* and image/png).
*
* In that situation, you can call this method to fill in the handler info
* object with information about that other type by passing the other type
* as the aOverrideType parameter.
*
* @param aHandlerInfo the handler info object
* @param aOverrideType a type to use instead of aHandlerInfo::type
*
* Note: if there is no information in the datastore about this type,
* this method throws NS_ERROR_NOT_AVAILABLE. Callers are encouraged to
* check exists() before calling fillHandlerInfo(), to prevent spamming the
* console with XPCOM exception errors.
*/
void fillHandlerInfo(in nsIHandlerInfo aHandlerInfo,
in ACString aOverrideType);
/**
* Save the preferred action, preferred handler, possible handlers, and
* always ask properties of the given handler info object to the datastore.
* Updates an existing record or creates a new one if necessary.
*
* Note: if preferred action is undefined or invalid, then we assume
* the default value nsIHandlerInfo::useHelperApp.
*
* @param aHandlerInfo the handler info object
*/
void store(in nsIHandlerInfo aHandlerInfo);
/**
* Whether or not a record for the given handler info object exists
* in the datastore. If the datastore is corrupt (or some other error
* is caught in the implementation), false will be returned.
*
* @param aHandlerInfo a handler info object
*
* @returns whether or not a record exists
*/
boolean exists(in nsIHandlerInfo aHandlerInfo);
/**
* Remove the given handler info object from the datastore. Deletes all
* records associated with the object, including the preferred handler, info,
* and type records plus the entry in the list of types, if they exist.
* Otherwise, it does nothing and does not return an error.
*
* @param aHandlerInfo the handler info object
*/
void remove(in nsIHandlerInfo aHandlerInfo);
/**
* Get the MIME type mapped to the given file extension in the datastore.
*
* XXX If we ever support extension -> protocol scheme mappings, then this
* method should work for those as well.
*
* Note: in general, you should use nsIMIMEService::getTypeFromExtension
* to get a MIME type from a file extension, as that method checks a variety
* of other sources besides just the datastore. Use this only when you want
* to specifically get only the mapping available in the datastore.
*
* @param aFileExtension the file extension
*
* @returns the MIME type, if any; otherwise returns an empty string ("").
*/
ACString getTypeFromExtension(in ACString aFileExtension);
};

View file

@ -0,0 +1,89 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
interface nsIHelperAppLauncher;
interface nsIFile;
/**
* This interface is used to display a confirmation dialog before
* launching a "helper app" to handle content not handled by
* Mozilla.
*
* Usage: Clients (of which there is one: the nsIExternalHelperAppService
* implementation in mozilla/uriloader/exthandler) create an instance of
* this interface (using the contract ID) and then call the show() method.
*
* The dialog is shown non-modally. The implementation of the dialog
* will access methods of the nsIHelperAppLauncher passed in to show()
* in order to cause a "save to disk" or "open using" action.
*/
[scriptable, uuid(bfc739f3-8d75-4034-a6f8-1039a5996bad)]
interface nsIHelperAppLauncherDialog : nsISupports {
/**
* This request is passed to the helper app dialog because Gecko can not
* handle content of this type.
*/
const unsigned long REASON_CANTHANDLE = 0;
/**
* The server requested external handling.
*/
const unsigned long REASON_SERVERREQUEST = 1;
/**
* Gecko detected that the type sent by the server (e.g. text/plain) does
* not match the actual type.
*/
const unsigned long REASON_TYPESNIFFED = 2;
/**
* Show confirmation dialog for launching application (or "save to
* disk") for content specified by aLauncher.
*
* @param aLauncher
* A nsIHelperAppLauncher to be invoked when a file is selected.
* @param aWindowContext
* Window associated with action.
* @param aReason
* One of the constants from above. It indicates why the dialog is
* shown. Implementors should treat unknown reasons like
* REASON_CANTHANDLE.
*/
void show(in nsIHelperAppLauncher aLauncher,
in nsISupports aWindowContext,
in unsigned long aReason);
/**
* Async invoke a save-to-file dialog instead of the full fledged helper app
* dialog. When the file is chosen (or the dialog is closed), the callback
* in aLauncher (aLauncher.saveDestinationAvailable) is called with the
* selected file.
*
* @param aLauncher
* A nsIHelperAppLauncher to be invoked when a file is selected.
* @param aWindowContext
* Window associated with action.
* @param aDefaultFileName
* Default file name to provide (can be null)
* @param aSuggestedFileExtension
* Sugested file extension
* @param aForcePrompt
* Set to true to force prompting the user for thet file
* name/location, otherwise perferences may control if the user is
* prompted.
*/
void promptForSaveToFileAsync(in nsIHelperAppLauncher aLauncher,
in nsISupports aWindowContext,
in wstring aDefaultFileName,
in wstring aSuggestedFileExtension,
in boolean aForcePrompt);
};
%{C++
#define NS_HELPERAPPLAUNCHERDLG_CONTRACTID "@mozilla.org/helperapplauncherdialog;1"
%}

View file

@ -0,0 +1,179 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 "nsLocalHandlerApp.h"
#include "nsIURI.h"
#include "nsIProcess.h"
// XXX why does nsMIMEInfoImpl have a threadsafe nsISupports? do we need one
// here too?
NS_IMPL_ISUPPORTS(nsLocalHandlerApp, nsILocalHandlerApp, nsIHandlerApp)
////////////////////////////////////////////////////////////////////////////////
//// nsIHandlerApp
NS_IMETHODIMP nsLocalHandlerApp::GetName(nsAString& aName)
{
if (mName.IsEmpty() && mExecutable) {
// Don't want to cache this, just in case someone resets the app
// without changing the description....
mExecutable->GetLeafName(aName);
} else {
aName.Assign(mName);
}
return NS_OK;
}
NS_IMETHODIMP nsLocalHandlerApp::SetName(const nsAString & aName)
{
mName.Assign(aName);
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::SetDetailedDescription(const nsAString & aDescription)
{
mDetailedDescription.Assign(aDescription);
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::GetDetailedDescription(nsAString& aDescription)
{
aDescription.Assign(mDetailedDescription);
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::Equals(nsIHandlerApp *aHandlerApp, bool *_retval)
{
NS_ENSURE_ARG_POINTER(aHandlerApp);
*_retval = false;
// If the handler app isn't a local handler app, then it's not the same app.
nsCOMPtr <nsILocalHandlerApp> localHandlerApp = do_QueryInterface(aHandlerApp);
if (!localHandlerApp)
return NS_OK;
// If either handler app doesn't have an executable, then they aren't
// the same app.
nsCOMPtr<nsIFile> executable;
nsresult rv = localHandlerApp->GetExecutable(getter_AddRefs(executable));
if (NS_FAILED(rv))
return rv;
// Equality for two empty nsIHandlerApp
if (!executable && !mExecutable) {
*_retval = true;
return NS_OK;
}
// At least one is set so they are not equal
if (!mExecutable || !executable)
return NS_OK;
// Check the command line parameter list lengths
uint32_t len;
localHandlerApp->GetParameterCount(&len);
if (mParameters.Length() != len)
return NS_OK;
// Check the command line params lists
for (uint32_t idx = 0; idx < mParameters.Length(); idx++) {
nsAutoString param;
if (NS_FAILED(localHandlerApp->GetParameter(idx, param)) ||
!param.Equals(mParameters[idx]))
return NS_OK;
}
return executable->Equals(mExecutable, _retval);
}
NS_IMETHODIMP
nsLocalHandlerApp::LaunchWithURI(nsIURI *aURI,
nsIInterfaceRequestor *aWindowContext)
{
// pass the entire URI to the handler.
nsAutoCString spec;
aURI->GetAsciiSpec(spec);
return LaunchWithIProcess(spec);
}
nsresult
nsLocalHandlerApp::LaunchWithIProcess(const nsCString& aArg)
{
nsresult rv;
nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
if (NS_FAILED(rv = process->Init(mExecutable)))
return rv;
const char *string = aArg.get();
return process->Run(false, &string, 1);
}
////////////////////////////////////////////////////////////////////////////////
//// nsILocalHandlerApp
NS_IMETHODIMP
nsLocalHandlerApp::GetExecutable(nsIFile **aExecutable)
{
NS_IF_ADDREF(*aExecutable = mExecutable);
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::SetExecutable(nsIFile *aExecutable)
{
mExecutable = aExecutable;
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::GetParameterCount(uint32_t *aParameterCount)
{
*aParameterCount = mParameters.Length();
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::ClearParameters()
{
mParameters.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::AppendParameter(const nsAString & aParam)
{
mParameters.AppendElement(aParam);
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::GetParameter(uint32_t parameterIndex, nsAString & _retval)
{
if (mParameters.Length() <= parameterIndex)
return NS_ERROR_INVALID_ARG;
_retval.Assign(mParameters[parameterIndex]);
return NS_OK;
}
NS_IMETHODIMP
nsLocalHandlerApp::ParameterExists(const nsAString & aParam, bool *_retval)
{
*_retval = mParameters.Contains(aParam);
return NS_OK;
}

View file

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 __nsLocalHandlerAppImpl_h__
#define __nsLocalHandlerAppImpl_h__
#include "nsString.h"
#include "nsIMIMEInfo.h"
#include "nsIFile.h"
#include "nsTArray.h"
class nsLocalHandlerApp : public nsILocalHandlerApp
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHANDLERAPP
NS_DECL_NSILOCALHANDLERAPP
nsLocalHandlerApp() { }
nsLocalHandlerApp(const char16_t *aName, nsIFile *aExecutable)
: mName(aName), mExecutable(aExecutable) { }
nsLocalHandlerApp(const nsAString & aName, nsIFile *aExecutable)
: mName(aName), mExecutable(aExecutable) { }
protected:
virtual ~nsLocalHandlerApp() { }
nsString mName;
nsString mDetailedDescription;
nsTArray<nsString> mParameters;
nsCOMPtr<nsIFile> mExecutable;
/**
* Launches this application with a single argument (typically either
* a file path or a URI spec). This is meant as a helper method for
* implementations of (e.g.) LaunchWithURI.
*
* @param aApp The application to launch (may not be null)
* @param aArg The argument to pass on the command line
*/
nsresult LaunchWithIProcess(const nsCString &aArg);
};
// any platforms that need a platform-specific class instead of just
// using nsLocalHandlerApp need to add an include and a typedef here.
#ifdef XP_MACOSX
# ifndef NSLOCALHANDLERAPPMAC_H_
# include "mac/nsLocalHandlerAppMac.h"
typedef nsLocalHandlerAppMac PlatformLocalHandlerApp_t;
# endif
#else
typedef nsLocalHandlerApp PlatformLocalHandlerApp_t;
#endif
#endif // __nsLocalHandlerAppImpl_h__

View file

@ -0,0 +1,435 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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 "nsMIMEInfoImpl.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsStringEnumerator.h"
#include "nsIFile.h"
#include "nsIFileURL.h"
#include "nsEscape.h"
#include "nsIURILoader.h"
#include "nsCURILoader.h"
// nsISupports methods
NS_IMPL_ADDREF(nsMIMEInfoBase)
NS_IMPL_RELEASE(nsMIMEInfoBase)
NS_INTERFACE_MAP_BEGIN(nsMIMEInfoBase)
NS_INTERFACE_MAP_ENTRY(nsIHandlerInfo)
// This is only an nsIMIMEInfo if it's a MIME handler.
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMIMEInfo, mClass == eMIMEInfo)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHandlerInfo)
NS_INTERFACE_MAP_END_THREADSAFE
// nsMIMEInfoImpl methods
// Constructors for a MIME handler.
nsMIMEInfoBase::nsMIMEInfoBase(const char *aMIMEType) :
mSchemeOrType(aMIMEType),
mClass(eMIMEInfo),
mPreferredAction(nsIMIMEInfo::saveToDisk),
mAlwaysAskBeforeHandling(true)
{
}
nsMIMEInfoBase::nsMIMEInfoBase(const nsACString& aMIMEType) :
mSchemeOrType(aMIMEType),
mClass(eMIMEInfo),
mPreferredAction(nsIMIMEInfo::saveToDisk),
mAlwaysAskBeforeHandling(true)
{
}
// Constructor for a handler that lets the caller specify whether this is a
// MIME handler or a protocol handler. In the long run, these will be distinct
// classes (f.e. nsMIMEInfo and nsProtocolInfo), but for now we reuse this class
// for both and distinguish between the two kinds of handlers via the aClass
// argument to this method, which can be either eMIMEInfo or eProtocolInfo.
nsMIMEInfoBase::nsMIMEInfoBase(const nsACString& aType, HandlerClass aClass) :
mSchemeOrType(aType),
mClass(aClass),
mPreferredAction(nsIMIMEInfo::saveToDisk),
mAlwaysAskBeforeHandling(true)
{
}
nsMIMEInfoBase::~nsMIMEInfoBase()
{
}
NS_IMETHODIMP
nsMIMEInfoBase::GetFileExtensions(nsIUTF8StringEnumerator** aResult)
{
return NS_NewUTF8StringEnumerator(aResult, &mExtensions, this);
}
NS_IMETHODIMP
nsMIMEInfoBase::ExtensionExists(const nsACString& aExtension, bool *_retval)
{
NS_ASSERTION(!aExtension.IsEmpty(), "no extension");
bool found = false;
uint32_t extCount = mExtensions.Length();
if (extCount < 1) return NS_OK;
for (uint8_t i=0; i < extCount; i++) {
const nsCString& ext = mExtensions[i];
if (ext.Equals(aExtension, nsCaseInsensitiveCStringComparator())) {
found = true;
break;
}
}
*_retval = found;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetPrimaryExtension(nsACString& _retval)
{
if (!mExtensions.Length())
return NS_ERROR_NOT_INITIALIZED;
_retval = mExtensions[0];
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::SetPrimaryExtension(const nsACString& aExtension)
{
NS_ASSERTION(!aExtension.IsEmpty(), "no extension");
uint32_t extCount = mExtensions.Length();
uint8_t i;
bool found = false;
for (i=0; i < extCount; i++) {
const nsCString& ext = mExtensions[i];
if (ext.Equals(aExtension, nsCaseInsensitiveCStringComparator())) {
found = true;
break;
}
}
if (found) {
mExtensions.RemoveElementAt(i);
}
mExtensions.InsertElementAt(0, aExtension);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::AppendExtension(const nsACString& aExtension)
{
mExtensions.AppendElement(aExtension);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetType(nsACString& aType)
{
if (mSchemeOrType.IsEmpty())
return NS_ERROR_NOT_INITIALIZED;
aType = mSchemeOrType;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetMIMEType(nsACString& aMIMEType)
{
if (mSchemeOrType.IsEmpty())
return NS_ERROR_NOT_INITIALIZED;
aMIMEType = mSchemeOrType;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetDescription(nsAString& aDescription)
{
aDescription = mDescription;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::SetDescription(const nsAString& aDescription)
{
mDescription = aDescription;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::Equals(nsIMIMEInfo *aMIMEInfo, bool *_retval)
{
if (!aMIMEInfo) return NS_ERROR_NULL_POINTER;
nsAutoCString type;
nsresult rv = aMIMEInfo->GetMIMEType(type);
if (NS_FAILED(rv)) return rv;
*_retval = mSchemeOrType.Equals(type);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::SetFileExtensions(const nsACString& aExtensions)
{
mExtensions.Clear();
nsCString extList( aExtensions );
int32_t breakLocation = -1;
while ( (breakLocation= extList.FindChar(',') )!= -1)
{
mExtensions.AppendElement(Substring(extList.get(), extList.get() + breakLocation));
extList.Cut(0, breakLocation+1 );
}
if ( !extList.IsEmpty() )
mExtensions.AppendElement( extList );
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetDefaultDescription(nsAString& aDefaultDescription)
{
aDefaultDescription = mDefaultAppDescription;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetPreferredApplicationHandler(nsIHandlerApp ** aPreferredAppHandler)
{
*aPreferredAppHandler = mPreferredApplication;
NS_IF_ADDREF(*aPreferredAppHandler);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::SetPreferredApplicationHandler(nsIHandlerApp * aPreferredAppHandler)
{
mPreferredApplication = aPreferredAppHandler;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetPossibleApplicationHandlers(nsIMutableArray ** aPossibleAppHandlers)
{
if (!mPossibleApplications)
mPossibleApplications = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (!mPossibleApplications)
return NS_ERROR_OUT_OF_MEMORY;
*aPossibleAppHandlers = mPossibleApplications;
NS_IF_ADDREF(*aPossibleAppHandlers);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetPreferredAction(nsHandlerInfoAction * aPreferredAction)
{
*aPreferredAction = mPreferredAction;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::SetPreferredAction(nsHandlerInfoAction aPreferredAction)
{
mPreferredAction = aPreferredAction;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::GetAlwaysAskBeforeHandling(bool * aAlwaysAsk)
{
*aAlwaysAsk = mAlwaysAskBeforeHandling;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::SetAlwaysAskBeforeHandling(bool aAlwaysAsk)
{
mAlwaysAskBeforeHandling = aAlwaysAsk;
return NS_OK;
}
/* static */
nsresult
nsMIMEInfoBase::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aFile)
{
nsresult rv;
nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(aURI, &rv);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIFile> file;
rv = fileUrl->GetFile(getter_AddRefs(file));
if (NS_FAILED(rv)) {
return rv;
}
file.forget(aFile);
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoBase::LaunchWithFile(nsIFile* aFile)
{
nsresult rv;
// it doesn't make any sense to call this on protocol handlers
NS_ASSERTION(mClass == eMIMEInfo,
"nsMIMEInfoBase should have mClass == eMIMEInfo");
if (mPreferredAction == useSystemDefault) {
return LaunchDefaultWithFile(aFile);
}
if (mPreferredAction == useHelperApp) {
if (!mPreferredApplication)
return NS_ERROR_FILE_NOT_FOUND;
// at the moment, we only know how to hand files off to local handlers
nsCOMPtr<nsILocalHandlerApp> localHandler =
do_QueryInterface(mPreferredApplication, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> executable;
rv = localHandler->GetExecutable(getter_AddRefs(executable));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString path;
aFile->GetNativePath(path);
return LaunchWithIProcess(executable, path);
}
return NS_ERROR_INVALID_ARG;
}
NS_IMETHODIMP
nsMIMEInfoBase::LaunchWithURI(nsIURI* aURI,
nsIInterfaceRequestor* aWindowContext)
{
// for now, this is only being called with protocol handlers; that
// will change once we get to more general registerContentHandler
// support
NS_ASSERTION(mClass == eProtocolInfo,
"nsMIMEInfoBase should be a protocol handler");
if (mPreferredAction == useSystemDefault) {
return LoadUriInternal(aURI);
}
if (mPreferredAction == useHelperApp) {
if (!mPreferredApplication)
return NS_ERROR_FILE_NOT_FOUND;
return mPreferredApplication->LaunchWithURI(aURI, aWindowContext);
}
return NS_ERROR_INVALID_ARG;
}
void
nsMIMEInfoBase::CopyBasicDataTo(nsMIMEInfoBase* aOther)
{
aOther->mSchemeOrType = mSchemeOrType;
aOther->mDefaultAppDescription = mDefaultAppDescription;
aOther->mExtensions = mExtensions;
}
/* static */
already_AddRefed<nsIProcess>
nsMIMEInfoBase::InitProcess(nsIFile* aApp, nsresult* aResult)
{
NS_ASSERTION(aApp, "Unexpected null pointer, fix caller");
nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID,
aResult);
if (NS_FAILED(*aResult))
return nullptr;
*aResult = process->Init(aApp);
if (NS_FAILED(*aResult))
return nullptr;
return process.forget();
}
/* static */
nsresult
nsMIMEInfoBase::LaunchWithIProcess(nsIFile* aApp, const nsCString& aArg)
{
nsresult rv;
nsCOMPtr<nsIProcess> process = InitProcess(aApp, &rv);
if (NS_FAILED(rv))
return rv;
const char *string = aArg.get();
return process->Run(false, &string, 1);
}
/* static */
nsresult
nsMIMEInfoBase::LaunchWithIProcess(nsIFile* aApp, const nsString& aArg)
{
nsresult rv;
nsCOMPtr<nsIProcess> process = InitProcess(aApp, &rv);
if (NS_FAILED(rv))
return rv;
const char16_t *string = aArg.get();
return process->Runw(false, &string, 1);
}
// nsMIMEInfoImpl implementation
NS_IMETHODIMP
nsMIMEInfoImpl::GetDefaultDescription(nsAString& aDefaultDescription)
{
if (mDefaultAppDescription.IsEmpty() && mDefaultApplication) {
// Don't want to cache this, just in case someone resets the app
// without changing the description....
mDefaultApplication->GetLeafName(aDefaultDescription);
} else {
aDefaultDescription = mDefaultAppDescription;
}
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInfoImpl::GetHasDefaultHandler(bool * _retval)
{
*_retval = !mDefaultAppDescription.IsEmpty();
if (mDefaultApplication) {
bool exists;
*_retval = NS_SUCCEEDED(mDefaultApplication->Exists(&exists)) && exists;
}
return NS_OK;
}
nsresult
nsMIMEInfoImpl::LaunchDefaultWithFile(nsIFile* aFile)
{
if (!mDefaultApplication)
return NS_ERROR_FILE_NOT_FOUND;
nsAutoCString nativePath;
aFile->GetNativePath(nativePath);
return LaunchWithIProcess(mDefaultApplication, nativePath);
}
NS_IMETHODIMP
nsMIMEInfoBase::GetPossibleLocalHandlers(nsIArray **_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

View file

@ -0,0 +1,196 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 et: */
/* 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 __nsmimeinfoimpl_h___
#define __nsmimeinfoimpl_h___
#include "nsIMIMEInfo.h"
#include "nsIAtom.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsIMutableArray.h"
#include "nsIFile.h"
#include "nsCOMPtr.h"
#include "nsIURI.h"
#include "nsIProcess.h"
/**
* UTF8 moz-icon URI string for the default handler application's icon, if
* available.
*/
#define PROPERTY_DEFAULT_APP_ICON_URL "defaultApplicationIconURL"
/**
* UTF8 moz-icon URI string for the user's preferred handler application's
* icon, if available.
*/
#define PROPERTY_CUSTOM_APP_ICON_URL "customApplicationIconURL"
/**
* Basic implementation of nsIMIMEInfo. Incomplete - it is meant to be
* subclassed, and GetHasDefaultHandler as well as LaunchDefaultWithFile need to
* be implemented.
*/
class nsMIMEInfoBase : public nsIMIMEInfo {
public:
NS_DECL_THREADSAFE_ISUPPORTS
// I'd use NS_DECL_NSIMIMEINFO, but I don't want GetHasDefaultHandler
NS_IMETHOD GetFileExtensions(nsIUTF8StringEnumerator **_retval) override;
NS_IMETHOD SetFileExtensions(const nsACString & aExtensions) override;
NS_IMETHOD ExtensionExists(const nsACString & aExtension, bool *_retval) override;
NS_IMETHOD AppendExtension(const nsACString & aExtension) override;
NS_IMETHOD GetPrimaryExtension(nsACString & aPrimaryExtension) override;
NS_IMETHOD SetPrimaryExtension(const nsACString & aPrimaryExtension) override;
NS_IMETHOD GetType(nsACString & aType) override;
NS_IMETHOD GetMIMEType(nsACString & aMIMEType) override;
NS_IMETHOD GetDescription(nsAString & aDescription) override;
NS_IMETHOD SetDescription(const nsAString & aDescription) override;
NS_IMETHOD Equals(nsIMIMEInfo *aMIMEInfo, bool *_retval) override;
NS_IMETHOD GetPreferredApplicationHandler(nsIHandlerApp * *aPreferredAppHandler) override;
NS_IMETHOD SetPreferredApplicationHandler(nsIHandlerApp * aPreferredAppHandler) override;
NS_IMETHOD GetPossibleApplicationHandlers(nsIMutableArray * *aPossibleAppHandlers) override;
NS_IMETHOD GetDefaultDescription(nsAString & aDefaultDescription) override;
NS_IMETHOD LaunchWithFile(nsIFile *aFile) override;
NS_IMETHOD LaunchWithURI(nsIURI *aURI,
nsIInterfaceRequestor *aWindowContext) override;
NS_IMETHOD GetPreferredAction(nsHandlerInfoAction *aPreferredAction) override;
NS_IMETHOD SetPreferredAction(nsHandlerInfoAction aPreferredAction) override;
NS_IMETHOD GetAlwaysAskBeforeHandling(bool *aAlwaysAskBeforeHandling) override;
NS_IMETHOD SetAlwaysAskBeforeHandling(bool aAlwaysAskBeforeHandling) override;
NS_IMETHOD GetPossibleLocalHandlers(nsIArray **_retval) override;
enum HandlerClass {
eMIMEInfo,
eProtocolInfo
};
// nsMIMEInfoBase methods
explicit nsMIMEInfoBase(const char *aMIMEType = "");
explicit nsMIMEInfoBase(const nsACString& aMIMEType);
nsMIMEInfoBase(const nsACString& aType, HandlerClass aClass);
void SetMIMEType(const nsACString & aMIMEType) { mSchemeOrType = aMIMEType; }
void SetDefaultDescription(const nsString& aDesc) { mDefaultAppDescription = aDesc; }
/**
* Copies basic data of this MIME Info Implementation to the given other
* MIME Info. The data consists of the MIME Type, the (default) description,
* the MacOS type and creator, and the extension list (this object's
* extension list will replace aOther's list, not append to it). This
* function also ensures that aOther's primary extension will be the same as
* the one of this object.
*/
void CopyBasicDataTo(nsMIMEInfoBase* aOther);
/**
* Return whether this MIMEInfo has any extensions
*/
bool HasExtensions() const { return mExtensions.Length() != 0; }
protected:
virtual ~nsMIMEInfoBase(); // must be virtual, as the the base class's Release should call the subclass's destructor
/**
* Launch the default application for the given file.
* For even more control over the launching, override launchWithFile.
* Also see the comment about nsIMIMEInfo in general, above.
*
* @param aFile The file that should be opened
*/
virtual nsresult LaunchDefaultWithFile(nsIFile* aFile) = 0;
/**
* Loads the URI with the OS default app.
*
* @param aURI The URI to pass off to the OS.
*/
virtual nsresult LoadUriInternal(nsIURI *aURI) = 0;
static already_AddRefed<nsIProcess> InitProcess(nsIFile* aApp,
nsresult* aResult);
/**
* This method can be used to launch the file or URI with a single
* argument (typically either a file path or a URI spec). This is
* meant as a helper method for implementations of
* LaunchWithURI/LaunchDefaultWithFile.
*
* @param aApp The application to launch (may not be null)
* @param aArg The argument to pass on the command line
*/
static nsresult LaunchWithIProcess(nsIFile* aApp,
const nsCString &aArg);
static nsresult LaunchWithIProcess(nsIFile* aApp,
const nsString &aArg);
/**
* Given a file: nsIURI, return the associated nsIFile
*
* @param aURI the file: URI in question
* @param aFile the associated nsIFile (out param)
*/
static nsresult GetLocalFileFromURI(nsIURI *aURI,
nsIFile **aFile);
// member variables
nsTArray<nsCString> mExtensions; ///< array of file extensions associated w/ this MIME obj
nsString mDescription; ///< human readable description
nsCString mSchemeOrType;
HandlerClass mClass;
nsCOMPtr<nsIHandlerApp> mPreferredApplication;
nsCOMPtr<nsIMutableArray> mPossibleApplications;
nsHandlerInfoAction mPreferredAction; ///< preferred action to associate with this type
nsString mPreferredAppDescription;
nsString mDefaultAppDescription;
bool mAlwaysAskBeforeHandling;
};
/**
* This is a complete implementation of nsIMIMEInfo, and contains all necessary
* methods. However, depending on your platform you may want to use a different
* way of launching applications. This class stores the default application in a
* member variable and provides a function for setting it. For local
* applications, launching is done using nsIProcess, native path of the file to
* open as first argument.
*/
class nsMIMEInfoImpl : public nsMIMEInfoBase {
public:
explicit nsMIMEInfoImpl(const char *aMIMEType = "") : nsMIMEInfoBase(aMIMEType) {}
explicit nsMIMEInfoImpl(const nsACString& aMIMEType) : nsMIMEInfoBase(aMIMEType) {}
nsMIMEInfoImpl(const nsACString& aType, HandlerClass aClass) :
nsMIMEInfoBase(aType, aClass) {}
virtual ~nsMIMEInfoImpl() {}
// nsIMIMEInfo methods
NS_IMETHOD GetHasDefaultHandler(bool *_retval);
NS_IMETHOD GetDefaultDescription(nsAString& aDefaultDescription);
// additional methods
/**
* Sets the default application. Supposed to be only called by the OS Helper
* App Services; the default application is immutable after it is first set.
*/
void SetDefaultApplication(nsIFile* aApp) { if (!mDefaultApplication) mDefaultApplication = aApp; }
protected:
// nsMIMEInfoBase methods
/**
* The base class implementation is to use LaunchWithIProcess in combination
* with mDefaultApplication. Subclasses can override that behaviour.
*/
virtual nsresult LaunchDefaultWithFile(nsIFile* aFile);
/**
* Loads the URI with the OS default app. This should be overridden by each
* OS's implementation.
*/
virtual nsresult LoadUriInternal(nsIURI *aURI) = 0;
nsCOMPtr<nsIFile> mDefaultApplication; ///< default application associated with this type.
};
#endif //__nsmimeinfoimpl_h___

View file

@ -0,0 +1,172 @@
/* 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/. */
////////////////////////////////////////////////////////////////////////////////
//// Constants
const Ci = Components.interfaces;
const Cr = Components.results;
const Cc = Components.classes;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
////////////////////////////////////////////////////////////////////////////////
//// nsWebHandler class
function nsWebHandlerApp() {}
nsWebHandlerApp.prototype = {
//////////////////////////////////////////////////////////////////////////////
//// nsWebHandler
classDescription: "A web handler for protocols and content",
classID: Components.ID("8b1ae382-51a9-4972-b930-56977a57919d"),
contractID: "@mozilla.org/uriloader/web-handler-app;1",
_name: null,
_detailedDescription: null,
_uriTemplate: null,
//////////////////////////////////////////////////////////////////////////////
//// nsIHandlerApp
get name() {
return this._name;
},
set name(aName) {
this._name = aName;
},
get detailedDescription() {
return this._detailedDescription;
},
set detailedDescription(aDesc) {
this._detailedDescription = aDesc;
},
equals: function(aHandlerApp) {
if (!aHandlerApp)
throw Cr.NS_ERROR_NULL_POINTER;
if (aHandlerApp instanceof Ci.nsIWebHandlerApp &&
aHandlerApp.uriTemplate &&
this.uriTemplate &&
aHandlerApp.uriTemplate == this.uriTemplate)
return true;
return false;
},
launchWithURI: function nWHA__launchWithURI(aURI, aWindowContext) {
// XXX need to strip passwd & username from URI to handle, as per the
// WhatWG HTML5 draft. nsSimpleURL, which is what we're going to get,
// can't do this directly. Ideally, we'd fix nsStandardURL to make it
// possible to turn off all of its quirks handling, and use that...
// encode the URI to be handled
var escapedUriSpecToHandle = encodeURIComponent(aURI.spec);
// insert the encoded URI and create the object version
var uriSpecToSend = this.uriTemplate.replace("%s", escapedUriSpecToHandle);
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var uriToSend = ioService.newURI(uriSpecToSend, null, null);
// if we have a window context, use the URI loader to load there
if (aWindowContext) {
try {
// getInterface throws if the object doesn't implement the given
// interface, so this try/catch statement is more of an if.
// If aWindowContext refers to a remote docshell, send the load
// request to the correct process.
aWindowContext.getInterface(Ci.nsIRemoteWindowContext)
.openURI(uriToSend);
return;
} catch (e) {
if (e.result != Cr.NS_NOINTERFACE) {
throw e;
}
}
// create a channel from this URI
var channel = NetUtil.newChannel({
uri: uriToSend,
loadUsingSystemPrincipal: true
});
channel.loadFlags = Ci.nsIChannel.LOAD_DOCUMENT_URI;
// load the channel
var uriLoader = Cc["@mozilla.org/uriloader;1"].
getService(Ci.nsIURILoader);
// XXX ideally, whether to pass the IS_CONTENT_PREFERRED flag should be
// passed in from above. Practically, the flag is probably a reasonable
// default since browsers don't care much, and link click is likely to be
// the more interesting case for non-browser apps. See
// <https://bugzilla.mozilla.org/show_bug.cgi?id=392957#c9> for details.
uriLoader.openURI(channel, Ci.nsIURILoader.IS_CONTENT_PREFERRED,
aWindowContext);
return;
}
// since we don't have a window context, hand it off to a browser
var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
// get browser dom window
var browserDOMWin = windowMediator.getMostRecentWindow("navigator:browser")
.QueryInterface(Ci.nsIDOMChromeWindow)
.browserDOMWindow;
// if we got an exception, there are several possible reasons why:
// a) this gecko embedding doesn't provide an nsIBrowserDOMWindow
// implementation (i.e. doesn't support browser-style functionality),
// so we need to kick the URL out to the OS default browser. This is
// the subject of bug 394479.
// b) this embedding does provide an nsIBrowserDOMWindow impl, but
// there doesn't happen to be a browser window open at the moment; one
// should be opened. It's not clear whether this situation will really
// ever occur in real life. If it does, the only API that I can find
// that seems reasonably likely to work for most embedders is the
// command line handler.
// c) something else went wrong
//
// it's not clear how one would differentiate between the three cases
// above, so for now we don't catch the exception
// openURI
browserDOMWin.openURI(uriToSend,
null, // no window.opener
Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
Ci.nsIBrowserDOMWindow.OPEN_NEW);
return;
},
//////////////////////////////////////////////////////////////////////////////
//// nsIWebHandlerApp
get uriTemplate() {
return this._uriTemplate;
},
set uriTemplate(aURITemplate) {
this._uriTemplate = aURITemplate;
},
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebHandlerApp, Ci.nsIHandlerApp])
};
////////////////////////////////////////////////////////////////////////////////
//// Module
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsWebHandlerApp]);

View file

@ -0,0 +1,2 @@
component {8b1ae382-51a9-4972-b930-56977a57919d} nsWebHandlerApp.js
contract @mozilla.org/uriloader/web-handler-app;1 {8b1ae382-51a9-4972-b930-56977a57919d}

View file

@ -0,0 +1,11 @@
# 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 $(topsrcdir)/config/rules.mk
# need the executable for running the xpcshell unit tests
ifneq (,$(SIMPLE_PROGRAMS))
libs::
$(INSTALL) $(SIMPLE_PROGRAMS) $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit
endif

View file

@ -0,0 +1,24 @@
#include <stdio.h>
#include "prenv.h"
int main(int argc, char* argv[])
{
if (argc != 2)
return 1;
const char* value = PR_GetEnv("WRITE_ARGUMENT_FILE");
if (!value)
return 2;
FILE* outfile = fopen(value, "w");
if (!outfile)
return 3;
// We only need to write out the first argument (no newline).
fputs(argv[argc -1], outfile);
fclose(outfile);
return 0;
}

View file

@ -0,0 +1,8 @@
[DEFAULT]
head = head.js
support-files =
protocolHandler.html
[browser_download_always_ask_preferred_app.js]
[browser_remember_download_option.js]
[browser_web_protocol_handlers.js]

View file

@ -0,0 +1,19 @@
add_task(function*() {
// Create mocked objects for test
let launcher = createMockedObjects(false);
// Open helper app dialog with mocked launcher
let dlg = yield* openHelperAppDialog(launcher);
let doc = dlg.document;
let location = doc.getElementById("source");
let expectedValue = launcher.source.prePath;
if (location.value != expectedValue) {
info("Waiting for dialog to be populated.");
yield BrowserTestUtils.waitForAttribute("value", location, expectedValue);
}
is(doc.getElementById("mode").selectedItem.id, "open", "Should be opening the file.");
ok(!dlg.document.getElementById("openHandler").selectedItem.hidden,
"Should not have selected a hidden item.");
let helperAppDialogHiddenPromise = BrowserTestUtils.windowClosed(dlg);
doc.documentElement.cancelDialog();
yield helperAppDialogHiddenPromise;
});

View file

@ -0,0 +1,49 @@
add_task(function*() {
// create mocked objects
let launcher = createMockedObjects(true);
// open helper app dialog with mocked launcher
let dlg = yield* openHelperAppDialog(launcher);
let doc = dlg.document;
// Set remember choice
ok(!doc.getElementById("rememberChoice").checked,
"Remember choice checkbox should be not checked.");
doc.getElementById("rememberChoice").checked = true;
// Make sure the mock handler information is not in nsIHandlerService
ok(!gHandlerSvc.exists(launcher.MIMEInfo), "Should not be in nsIHandlerService.");
// close the dialog by pushing the ok button.
let dialogClosedPromise = BrowserTestUtils.windowClosed(dlg);
// Make sure the ok button is enabled, since the ok button might be disabled by
// EnableDelayHelper mechanism. Please refer the detailed
// https://dxr.mozilla.org/mozilla-central/source/toolkit/components/prompts/src/SharedPromptUtils.jsm#53
doc.documentElement.getButton("accept").disabled = false;
doc.documentElement.acceptDialog();
yield dialogClosedPromise;
// check the mocked handler information is saved in nsIHandlerService
ok(gHandlerSvc.exists(launcher.MIMEInfo), "Should be in nsIHandlerService.");
// check the extension.
var mimeType = gHandlerSvc.getTypeFromExtension("abc");
is(mimeType, launcher.MIMEInfo.type, "Got correct mime type.");
var handlerInfos = gHandlerSvc.enumerate();
while (handlerInfos.hasMoreElements()) {
let handlerInfo = handlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
if (handlerInfo.type == launcher.MIMEInfo.type) {
// check the alwaysAskBeforeHandling
ok(!handlerInfo.alwaysAskBeforeHandling,
"Should turn off the always ask.");
// check the preferredApplicationHandler
ok(handlerInfo.preferredApplicationHandler.equals(
launcher.MIMEInfo.preferredApplicationHandler),
"Should be equal to the mockedHandlerApp.");
// check the perferredAction
is(handlerInfo.preferredAction, launcher.MIMEInfo.preferredAction,
"Should be equal to Ci.nsIHandlerInfo.useHelperApp.");
break;
}
}
});

View file

@ -0,0 +1,76 @@
let testURL = "http://example.com/browser/" +
"uriloader/exthandler/tests/mochitest/protocolHandler.html";
add_task(function*() {
// Load a page registering a protocol handler.
let browser = gBrowser.selectedBrowser;
browser.loadURI(testURL);
yield BrowserTestUtils.browserLoaded(browser, testURL);
// Register the protocol handler by clicking the notificationbar button.
let notificationValue = "Protocol Registration: testprotocol";
let getNotification = () =>
gBrowser.getNotificationBox().getNotificationWithValue(notificationValue);
yield BrowserTestUtils.waitForCondition(getNotification);
let notification = getNotification();
let button =
notification.getElementsByClassName("notification-button-default")[0];
ok(button, "got registration button");
button.click();
// Set the new handler as default.
const protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
getService(Ci.nsIExternalProtocolService);
let protoInfo = protoSvc.getProtocolHandlerInfo("testprotocol");
is(protoInfo.preferredAction, protoInfo.useHelperApp,
"using a helper application is the preferred action");
ok(!protoInfo.preferredApplicationHandler, "no preferred handler is set");
let handlers = protoInfo.possibleApplicationHandlers;
is(1, handlers.length, "only one handler registered for testprotocol");
let handler = handlers.queryElementAt(0, Ci.nsIHandlerApp);
ok(handler instanceof Ci.nsIWebHandlerApp, "the handler is a web handler");
is(handler.uriTemplate, "https://example.com/foobar?uri=%s",
"correct url template")
protoInfo.preferredApplicationHandler = handler;
protoInfo.alwaysAskBeforeHandling = false;
const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].
getService(Ci.nsIHandlerService);
handlerSvc.store(protoInfo);
// Middle-click a testprotocol link and check the new tab is correct
let link = "#link";
const expectedURL = "https://example.com/foobar?uri=testprotocol%3Atest";
let promiseTabOpened =
BrowserTestUtils.waitForNewTab(gBrowser, expectedURL);
yield BrowserTestUtils.synthesizeMouseAtCenter(link, {button: 1}, browser);
let tab = yield promiseTabOpened;
gBrowser.selectedTab = tab;
is(gURLBar.value, expectedURL,
"the expected URL is displayed in the location bar");
yield BrowserTestUtils.removeTab(tab);
// Shift-click the testprotocol link and check the new window.
let newWindowPromise = BrowserTestUtils.waitForNewWindow();
yield BrowserTestUtils.synthesizeMouseAtCenter(link, {shiftKey: true},
browser);
let win = yield newWindowPromise;
yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
yield BrowserTestUtils.waitForCondition(() => win.gBrowser.currentURI.spec == expectedURL);
is(win.gURLBar.value, expectedURL,
"the expected URL is displayed in the location bar");
yield BrowserTestUtils.closeWindow(win);
// Click the testprotocol link and check the url in the current tab.
let loadPromise = BrowserTestUtils.browserLoaded(browser);
yield BrowserTestUtils.synthesizeMouseAtCenter(link, {}, browser);
yield loadPromise;
yield BrowserTestUtils.waitForCondition(() => gURLBar.value != testURL);
is(gURLBar.value, expectedURL,
"the expected URL is displayed in the location bar");
// Cleanup.
protoInfo.preferredApplicationHandler = null;
handlers.removeElementAt(0);
handlerSvc.store(protoInfo);
});

View file

@ -0,0 +1,30 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Pseudo Web Handler App</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="onLoad()">
Pseudo Web Handler App
<script class="testbody" type="text/javascript">
<![CDATA[
function onLoad() {
// if we have a window.opener, this must be the windowContext
// instance of this test. check that we got the URI right and clean up.
if (window.opener) {
window.opener.is(location.search,
"?uri=" + encodeURIComponent(window.opener.testURI),
"uri passed to web-handler app");
window.opener.SimpleTest.finish();
}
window.close();
}
]]>
</script>
</body>
</html>

View file

@ -0,0 +1,110 @@
/* 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/. */
// handlerApp.xhtml grabs this for verification purposes via window.opener
var testURI = "webcal://127.0.0.1/rheeeeet.html";
const Cc = SpecialPowers.Cc;
function test() {
// set up the web handler object
var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
createInstance(SpecialPowers.Ci.nsIWebHandlerApp);
webHandler.name = "Test Web Handler App";
webHandler.uriTemplate =
"http://mochi.test:8888/tests/uriloader/exthandler/tests/mochitest/" +
"handlerApp.xhtml?uri=%s";
// set up the uri to test with
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(SpecialPowers.Ci.nsIIOService);
var uri = ioService.newURI(testURI, null, null);
// create a window, and launch the handler in it
var newWindow = window.open("", "handlerWindow", "height=300,width=300");
var windowContext =
SpecialPowers.wrap(newWindow).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
getInterface(SpecialPowers.Ci.nsIWebNavigation).
QueryInterface(SpecialPowers.Ci.nsIDocShell);
webHandler.launchWithURI(uri, windowContext);
// if we get this far without an exception, we've at least partly passed
// (remaining check in handlerApp.xhtml)
ok(true, "webHandler launchWithURI (existing window/tab) started");
// make the web browser launch in its own window/tab
webHandler.launchWithURI(uri);
// if we get this far without an exception, we've passed
ok(true, "webHandler launchWithURI (new window/tab) test started");
// set up the local handler object
var localHandler = Cc["@mozilla.org/uriloader/local-handler-app;1"].
createInstance(SpecialPowers.Ci.nsILocalHandlerApp);
localHandler.name = "Test Local Handler App";
// get a local app that we know will be there and do something sane
var osString = Cc["@mozilla.org/xre/app-info;1"].
getService(SpecialPowers.Ci.nsIXULRuntime).OS;
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(SpecialPowers.Ci.nsIDirectoryServiceProvider);
if (osString == "WINNT") {
var windowsDir = dirSvc.getFile("WinD", {});
var exe = windowsDir.clone().QueryInterface(SpecialPowers.Ci.nsILocalFile);
exe.appendRelativePath("SYSTEM32\\HOSTNAME.EXE");
} else if (osString == "Darwin") {
var localAppsDir = dirSvc.getFile("LocApp", {});
exe = localAppsDir.clone();
exe.append("iCal.app"); // lingers after the tests finish, but this seems
// seems better than explicitly killing it, since
// developers who run the tests locally may well
// information in their running copy of iCal
if (navigator.userAgent.match(/ SeaMonkey\//)) {
// SeaMonkey tinderboxes don't like to have iCal lingering (and focused)
// on next test suite run(s).
todo(false, "On SeaMonkey, testing OS X as generic Unix. (Bug 749872)");
// assume a generic UNIX variant
exe = Cc["@mozilla.org/file/local;1"].
createInstance(SpecialPowers.Ci.nsILocalFile);
exe.initWithPath("/bin/echo");
}
} else {
// assume a generic UNIX variant
exe = Cc["@mozilla.org/file/local;1"].
createInstance(SpecialPowers.Ci.nsILocalFile);
exe.initWithPath("/bin/echo");
}
localHandler.executable = exe;
localHandler.launchWithURI(ioService.newURI(testURI, null, null));
// if we get this far without an exception, we've passed
ok(true, "localHandler launchWithURI test");
// if we ever decide that killing iCal is the right thing to do, change
// the if statement below from "NOTDarwin" to "Darwin"
if (osString == "NOTDarwin") {
var killall = Cc["@mozilla.org/file/local;1"].
createInstance(SpecialPowers.Ci.nsILocalFile);
killall.initWithPath("/usr/bin/killall");
var process = Cc["@mozilla.org/process/util;1"].
createInstance(SpecialPowers.Ci.nsIProcess);
process.init(killall);
var args = ['iCal'];
process.run(false, args, args.length);
}
SimpleTest.waitForExplicitFinish();
}
test();

View file

@ -0,0 +1,105 @@
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
var gMimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
var gHandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService);
function createMockedHandlerApp() {
// Mock the executable
let mockedExecutable = FileUtils.getFile("TmpD", ["mockedExecutable"]);
if (!mockedExecutable.exists()) {
mockedExecutable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o755);
}
// Mock the handler app
let mockedHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]
.createInstance(Ci.nsILocalHandlerApp);
mockedHandlerApp.executable = mockedExecutable;
mockedHandlerApp.detailedDescription = "Mocked handler app";
registerCleanupFunction(function() {
// remove the mocked executable from disk.
if (mockedExecutable.exists()) {
mockedExecutable.remove(true);
}
});
return mockedHandlerApp;
}
function createMockedObjects(createHandlerApp) {
// Mock the mime info
let internalMockedMIME = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
internalMockedMIME.alwaysAskBeforeHandling = true;
internalMockedMIME.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
internalMockedMIME.appendExtension("abc");
if (createHandlerApp) {
let mockedHandlerApp = createMockedHandlerApp();
internalMockedMIME.description = mockedHandlerApp.detailedDescription;
internalMockedMIME.possibleApplicationHandlers.appendElement(mockedHandlerApp, false);
internalMockedMIME.preferredApplicationHandler = mockedHandlerApp;
}
// Proxy for the mocked MIME info for faking the read-only attributes
let mockedMIME = new Proxy(internalMockedMIME, {
get: function (target, property) {
switch (property) {
case "hasDefaultHandler":
return true;
case "defaultDescription":
return "Default description";
default:
return target[property];
}
},
});
// Mock the launcher:
let mockedLauncher = {
MIMEInfo: mockedMIME,
source: Services.io.newURI("http://www.mozilla.org/", null, null),
suggestedFileName: "test_download_dialog.abc",
targetFileIsExecutable: false,
saveToDisk() {},
cancel() {},
launchWithApplication() {},
setWebProgressListener() {},
saveDestinationAvailable() {},
contentLength: 42,
targetFile: null, // never read
// PRTime is microseconds since epoch, Date.now() returns milliseconds:
timeDownloadStarted: Date.now() * 1000,
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable, Ci.nsIHelperAppLauncher])
};
registerCleanupFunction(function() {
// remove the mocked mime info from database.
let mockHandlerInfo = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
if (gHandlerSvc.exists(mockHandlerInfo)) {
gHandlerSvc.remove(mockHandlerInfo);
}
});
return mockedLauncher;
}
function* openHelperAppDialog(launcher) {
let helperAppDialog = Cc["@mozilla.org/helperapplauncherdialog;1"].
createInstance(Ci.nsIHelperAppLauncherDialog);
let helperAppDialogShownPromise = BrowserTestUtils.domWindowOpened();
try {
helperAppDialog.show(launcher, window, "foopy");
} catch (ex) {
ok(false, "Trying to show unknownContentType.xul failed with exception: " + ex);
Cu.reportError(ex);
}
let dlg = yield helperAppDialogShownPromise;
yield BrowserTestUtils.waitForEvent(dlg, "load", false);
is(dlg.location.href, "chrome://mozapps/content/downloads/unknownContentType.xul",
"Got correct dialog");
return dlg;
}

View file

@ -0,0 +1,10 @@
[DEFAULT]
support-files =
handlerApp.xhtml
handlerApps.js
unsafeBidi_chromeScript.js
unsafeBidiFileName.sjs
[test_handlerApps.xhtml]
skip-if = (toolkit == 'android' || os == 'mac') || e10s # OS X: bug 786938
[test_unsafeBidiChars.xhtml]

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Protocol handler</title>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body>
<script type="text/javascript">
navigator.registerProtocolHandler("testprotocol",
"https://example.com/foobar?uri=%s",
"Test Protocol");
</script>
<a id="link" href="testprotocol:test">testprotocol link</a>
</body>
</html>

View file

@ -0,0 +1,12 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test for Handler Apps </title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="handlerApps.js"/>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
</body>
</html>

View file

@ -0,0 +1,77 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test for Handling of unsafe bidi chars</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<iframe id="test"></iframe>
<script type="text/javascript">
<![CDATA[
var unsafeBidiChars = [
"\xe2\x80\xaa", // LRE
"\xe2\x80\xab", // RLE
"\xe2\x80\xac", // PDF
"\xe2\x80\xad", // LRO
"\xe2\x80\xae" // RLO
];
var tests = [
"{1}.test",
"{1}File.test",
"Fi{1}le.test",
"File{1}.test",
"File.{1}test",
"File.te{1}st",
"File.test{1}",
"File.{1}",
];
function replace(name, x) {
return name.replace(/\{1\}/, x);
}
function sanitize(name) {
return replace(name, '_');
}
add_task(function* () {
let url = SimpleTest.getTestFileURL("unsafeBidi_chromeScript.js");
let chromeScript = SpecialPowers.loadChromeScript(url);
for (let test of tests) {
for (let char of unsafeBidiChars) {
let promiseName = new Promise(function(resolve) {
chromeScript.addMessageListener("suggestedFileName",
function listener(data) {
chromeScript.removeMessageListener("suggestedFileName", listener);
resolve(data);
});
});
let name = replace(test, char);
let expected = sanitize(test);
document.getElementById("test").src =
"unsafeBidiFileName.sjs?name=" + encodeURIComponent(name);
is((yield promiseName), expected, "got the expected sanitized name");
}
}
let promise = new Promise(function(resolve) {
chromeScript.addMessageListener("unregistered", function listener() {
chromeScript.removeMessageListener("unregistered", listener);
resolve();
});
});
chromeScript.sendAsyncMessage("unregister");
yield promise;
chromeScript.destroy();
});
]]>
</script>
</body>
</html>

View file

@ -0,0 +1,14 @@
/* 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/. */
function handleRequest(request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
if (!request.queryString.match(/^name=/))
return;
var name = decodeURIComponent(request.queryString.substring(5));
response.setHeader("Content-Type", "application/octet-stream; name=\"" + name + "\"");
response.setHeader("Content-Disposition", "inline; filename=\"" + name + "\"");
}

View file

@ -0,0 +1,28 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const HELPERAPP_DIALOG_CONTRACT = "@mozilla.org/helperapplauncherdialog;1";
const HELPERAPP_DIALOG_CID =
Components.ID(Cc[HELPERAPP_DIALOG_CONTRACT].number);
const FAKE_CID = Cc["@mozilla.org/uuid-generator;1"].
getService(Ci.nsIUUIDGenerator).generateUUID();
function HelperAppLauncherDialog() {}
HelperAppLauncherDialog.prototype = {
show: function(aLauncher, aWindowContext, aReason) {
sendAsyncMessage("suggestedFileName", aLauncher.suggestedFileName);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog])
};
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(FAKE_CID, "", HELPERAPP_DIALOG_CONTRACT,
XPCOMUtils._getFactory(HelperAppLauncherDialog));
addMessageListener("unregister", function() {
registrar.registerFactory(HELPERAPP_DIALOG_CID, "",
HELPERAPP_DIALOG_CONTRACT, null);
sendAsyncMessage("unregistered");
});

View file

@ -0,0 +1,24 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['mochitest/browser.ini']
# The encoding test is already implemented in the Downloads API by a set of
# test cases with the string "content_encoding" in their names.
if not CONFIG['MOZ_JSDOWNLOADS']:
XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
GeckoSimplePrograms([
'WriteArgument',
], linkage=None)
USE_LIBS += [
'nspr',
]

View file

@ -0,0 +1,163 @@
/* 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/. */
// Inspired by the Places infrastructure in head_bookmarks.js
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;
var HandlerServiceTest = {
//**************************************************************************//
// Convenience Getters
__dirSvc: null,
get _dirSvc() {
if (!this.__dirSvc)
this.__dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties).
QueryInterface(Ci.nsIDirectoryService);
return this.__dirSvc;
},
__consoleSvc: null,
get _consoleSvc() {
if (!this.__consoleSvc)
this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);
return this.__consoleSvc;
},
//**************************************************************************//
// nsISupports
interfaces: [Ci.nsIDirectoryServiceProvider, Ci.nsISupports],
QueryInterface: function HandlerServiceTest_QueryInterface(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
//**************************************************************************//
// Initialization & Destruction
init: function HandlerServiceTest_init() {
// Register ourselves as a directory provider for the datasource file
// if there isn't one registered already.
try {
this._dirSvc.get("UMimTyp", Ci.nsIFile);
} catch (ex) {
this._dirSvc.registerProvider(this);
this._providerRegistered = true;
}
// Delete the existing datasource file, if any, so we start from scratch.
// We also do this after finishing the tests, so there shouldn't be an old
// file lying around, but just in case we delete it here as well.
this._deleteDatasourceFile();
// Turn on logging so we can troubleshoot problems with the tests.
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefBranch.setBoolPref("browser.contentHandling.log", true);
},
destroy: function HandlerServiceTest_destroy() {
// Delete the existing datasource file, if any, so we don't leave test files
// lying around and we start from scratch the next time.
this._deleteDatasourceFile();
// Unregister the directory service provider
if (this._providerRegistered)
this._dirSvc.unregisterProvider(this);
},
//**************************************************************************//
// nsIDirectoryServiceProvider
getFile: function HandlerServiceTest_getFile(property, persistent) {
this.log("getFile: requesting " + property);
persistent.value = true;
if (property == "UMimTyp") {
var datasourceFile = this._dirSvc.get("CurProcD", Ci.nsIFile);
datasourceFile.append("mimeTypes.rdf");
return datasourceFile;
}
// This causes extraneous errors to show up in the log when the directory
// service asks us first for CurProcD and MozBinD. I wish there was a way
// to suppress those errors.
this.log("the following NS_ERROR_FAILURE exception in " +
"nsIDirectoryServiceProvider::getFile is expected, " +
"as we don't provide the '" + property + "' file");
throw Cr.NS_ERROR_FAILURE;
},
//**************************************************************************//
// Utilities
/**
* Delete the datasource file.
*/
_deleteDatasourceFile: function HandlerServiceTest__deleteDatasourceFile() {
var file = this._dirSvc.get("UMimTyp", Ci.nsIFile);
if (file.exists())
file.remove(false);
},
/**
* Get the contents of the datasource as a serialized string. Useful for
* debugging problems with test failures, i.e.:
*
* HandlerServiceTest.log(HandlerServiceTest.getDatasourceContents());
*
* @returns {string} the serialized datasource
*/
getDatasourceContents: function HandlerServiceTest_getDatasourceContents() {
var rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var fileHandler = ioService.getProtocolHandler("file").
QueryInterface(Ci.nsIFileProtocolHandler);
var fileURL = fileHandler.getURLSpecFromFile(this.getDatasourceFile());
var ds = rdf.GetDataSourceBlocking(fileURL);
var outputStream = {
data: "",
close: function() {},
flush: function() {},
write: function (buffer,count) {
this.data += buffer;
return count;
},
writeFrom: function (stream,count) {},
isNonBlocking: false
};
ds.QueryInterface(Components.interfaces.nsIRDFXMLSource);
ds.Serialize(outputStream);
return outputStream.data;
},
/**
* Log a message to the console and the test log.
*/
log: function HandlerServiceTest_log(message) {
message = "*** HandlerServiceTest: " + message;
this._consoleSvc.logStringMessage(message);
print(message);
}
};
HandlerServiceTest.init();

View file

@ -0,0 +1,2 @@
text/plain; cat '%s'; needsterminal
text/plain; sed '%s'

View file

@ -0,0 +1,5 @@
/* 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/. */
HandlerServiceTest.destroy();

View file

@ -0,0 +1,26 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
function run_test() {
// "text/plain" has an 0xFF character appended to it. This means it's an
// invalid string, which is tricky to enter using a text editor (I used
// emacs' hexl-mode). It also means an ordinary text editor might drop it
// or convert it to something that *is* valid (in UTF8). So we measure
// its length to make sure this hasn't happened.
var badMimeType = "text/plainÿ";
do_check_eq(badMimeType.length, 11);
try {
var type = Cc["@mozilla.org/mime;1"].
getService(Ci.nsIMIMEService).
getFromTypeAndExtension(badMimeType, "txt");
} catch (e if (e instanceof Ci.nsIException &&
e.result == Cr.NS_ERROR_NOT_AVAILABLE)) {
// This is an expected exception, thrown if the type can't be determined
} finally {
}
// Not crashing is good enough
do_check_eq(true, true);
}

View file

@ -0,0 +1,53 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
/**
* Test for bug 508030 <https://bugzilla.mozilla.org/show_bug.cgi?id=508030>:
* nsIMIMEService.getTypeFromExtension fails to find a match in the
* "ext-to-type-mapping" category if the provided extension is not lowercase.
*/
function run_test() {
// --- Common services ---
const mimeService = Cc["@mozilla.org/mime;1"].
getService(Ci.nsIMIMEService);
const categoryManager = Cc["@mozilla.org/categorymanager;1"].
getService(Ci.nsICategoryManager);
// --- Test procedure ---
const kTestExtension = "testextension";
const kTestExtensionMixedCase = "testExtensIon";
const kTestMimeType = "application/x-testextension";
// Ensure that the test extension is not initially recognized by the operating
// system or the "ext-to-type-mapping" category.
try {
// Try and get the MIME type associated with the extension.
mimeService.getTypeFromExtension(kTestExtension);
// The line above should have thrown an exception.
do_throw("nsIMIMEService.getTypeFromExtension succeeded unexpectedly");
} catch (e if (e instanceof Ci.nsIException &&
e.result == Cr.NS_ERROR_NOT_AVAILABLE)) {
// This is an expected exception, thrown if the type can't be determined.
// Any other exception would cause the test to fail.
}
// Add a temporary category entry mapping the extension to the MIME type.
categoryManager.addCategoryEntry("ext-to-type-mapping", kTestExtension,
kTestMimeType, false, true);
// Check that the mapping is recognized in the simple case.
var type = mimeService.getTypeFromExtension(kTestExtension);
do_check_eq(type, kTestMimeType);
// Check that the mapping is recognized even if the extension has mixed case.
type = mimeService.getTypeFromExtension(kTestExtensionMixedCase);
do_check_eq(type, kTestMimeType);
// Clean up after ourselves.
categoryManager.deleteCategoryEntry("ext-to-type-mapping", kTestExtension, false);
}

View file

@ -0,0 +1,186 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
/**
* Test for bug 484579 <https://bugzilla.mozilla.org/show_bug.cgi?id=484579>:
* nsIMIMEService.getTypeFromExtension may fail unexpectedly on Windows when
* "Content Type" is empty in the registry.
*/
function run_test() {
// --- Preliminary platform check ---
// If this test is not running on the Windows platform, stop now, before
// calling XPCOMUtils.generateQI during the MockWindowsRegKey declaration.
if (mozinfo.os != "win")
return;
// --- Modified nsIWindowsRegKey implementation ---
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/**
* Constructs a new mock registry key by wrapping the provided object.
*
* This mock implementation is tailored for this test, and forces consumers
* of the readStringValue method to believe that the "Content Type" value of
* the ".txt" key under HKEY_CLASSES_ROOT is an empty string.
*
* The same value read from "HKEY_LOCAL_MACHINE\SOFTWARE\Classes" is not
* affected.
*
* @param aWrappedObject An actual nsIWindowsRegKey implementation.
*/
function MockWindowsRegKey(aWrappedObject) {
this._wrappedObject = aWrappedObject;
// This function creates a forwarding function for wrappedObject
function makeForwardingFunction(functionName) {
return function() {
return aWrappedObject[functionName].apply(aWrappedObject, arguments);
}
}
// Forward all the functions that are not explicitly overridden
for (var propertyName in aWrappedObject) {
if (!(propertyName in this)) {
if (typeof aWrappedObject[propertyName] == "function") {
this[propertyName] = makeForwardingFunction(propertyName);
} else {
this[propertyName] = aWrappedObject[propertyName];
}
}
}
}
MockWindowsRegKey.prototype = {
// --- Overridden nsISupports interface functions ---
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowsRegKey]),
// --- Overridden nsIWindowsRegKey interface functions ---
open: function(aRootKey, aRelPath, aMode) {
// Remember the provided root key and path
this._rootKey = aRootKey;
this._relPath = aRelPath;
// Create the actual registry key
return this._wrappedObject.open(aRootKey, aRelPath, aMode);
},
openChild: function(aRelPath, aMode) {
// Open the child key and wrap it
var innerKey = this._wrappedObject.openChild(aRelPath, aMode);
var key = new MockWindowsRegKey(innerKey);
// Set the properties of the child key and return it
key._rootKey = this._rootKey;
key._relPath = this._relPath + aRelPath;
return key;
},
createChild: function(aRelPath, aMode) {
// Create the child key and wrap it
var innerKey = this._wrappedObject.createChild(aRelPath, aMode);
var key = new MockWindowsRegKey(innerKey);
// Set the properties of the child key and return it
key._rootKey = this._rootKey;
key._relPath = this._relPath + aRelPath;
return key;
},
get childCount() {
return this._wrappedObject.childCount;
},
get valueCount() {
return this._wrappedObject.valueCount;
},
readStringValue: function(aName) {
// If this is the key under test, return a fake value
if (this._rootKey == Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT &&
this._relPath.toLowerCase() == ".txt" &&
aName.toLowerCase() == "content type") {
return "";
}
// Return the real value in the registry
return this._wrappedObject.readStringValue(aName);
}
};
// --- Mock nsIWindowsRegKey factory ---
var componentRegistrar = Components.manager.
QueryInterface(Ci.nsIComponentRegistrar);
var originalWindowsRegKeyCID;
var mockWindowsRegKeyFactory;
const kMockCID = Components.ID("{9b23dfe9-296b-4740-ba1c-d39c9a16e55e}");
const kWindowsRegKeyContractID = "@mozilla.org/windows-registry-key;1";
const kWindowsRegKeyClassName = "nsWindowsRegKey";
function registerMockWindowsRegKeyFactory() {
mockWindowsRegKeyFactory = {
createInstance: function(aOuter, aIid) {
if (aOuter != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
var innerKey = originalWindowsRegKeyFactory.createInstance(null, aIid);
var key = new MockWindowsRegKey(innerKey);
return key.QueryInterface(aIid);
}
};
// Preserve the original factory
originalWindowsRegKeyCID = Cc[kWindowsRegKeyContractID].number;
// Register the mock factory
componentRegistrar.registerFactory(
kMockCID,
"Mock Windows Registry Key Implementation",
kWindowsRegKeyContractID,
mockWindowsRegKeyFactory
);
}
function unregisterMockWindowsRegKeyFactory() {
// Free references to the mock factory
componentRegistrar.unregisterFactory(
kMockCID,
mockWindowsRegKeyFactory
);
// Restore the original factory
componentRegistrar.registerFactory(
Components.ID(originalWindowsRegKeyCID),
"",
kWindowsRegKeyContractID,
null
);
}
// --- Test procedure ---
// Activate the override of the ".txt" file association data in the registry
registerMockWindowsRegKeyFactory();
try {
// Try and get the MIME type associated with the extension. If this
// operation does not throw an unexpected exception, the test succeeds.
var type = Cc["@mozilla.org/mime;1"].
getService(Ci.nsIMIMEService).
getTypeFromExtension(".txt");
} catch (e if (e instanceof Ci.nsIException &&
e.result == Cr.NS_ERROR_NOT_AVAILABLE)) {
// This is an expected exception, thrown if the type can't be determined
} finally {
// Ensure we restore the original factory when the test is finished
unregisterMockWindowsRegKeyFactory();
}
}

View file

@ -0,0 +1,470 @@
/* 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/. */
function run_test() {
//**************************************************************************//
// Constants
const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].
getService(Ci.nsIHandlerService);
const mimeSvc = Cc["@mozilla.org/mime;1"].
getService(Ci.nsIMIMEService);
const protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
getService(Ci.nsIExternalProtocolService);
const prefSvc = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
const ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
const env = Cc["@mozilla.org/process/environment;1"].
getService(Components.interfaces.nsIEnvironment);
const rootPrefBranch = prefSvc.getBranch("");
let noMailto = false;
if (mozinfo.os == "win") {
// Check mailto handler from registry.
// If registry entry is nothing, no mailto handler
let regSvc = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(Ci.nsIWindowsRegKey);
try {
regSvc.open(regSvc.ROOT_KEY_CLASSES_ROOT,
"mailto",
regSvc.ACCESS_READ);
noMailto = false;
} catch (ex) {
noMailto = true;
}
regSvc.close();
}
if (mozinfo.os == "linux") {
// Check mailto handler from GIO
// If there isn't one, then we have no mailto handler
let gIOSvc = Cc["@mozilla.org/gio-service;1"].
createInstance(Ci.nsIGIOService);
try {
gIOSvc.getAppForURIScheme("mailto");
noMailto = false;
} catch (ex) {
noMailto = true;
}
}
//**************************************************************************//
// Sample Data
// It doesn't matter whether or not this nsIFile is actually executable,
// only that it has a path and exists. Since we don't know any executable
// that exists on all platforms (except possibly the application being
// tested, but there doesn't seem to be a way to get a reference to that
// from the directory service), we use the temporary directory itself.
var executable = HandlerServiceTest._dirSvc.get("TmpD", Ci.nsIFile);
// XXX We could, of course, create an actual executable in the directory:
//executable.append("localhandler");
//if (!executable.exists())
// executable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o755);
var localHandler = {
name: "Local Handler",
executable: executable,
interfaces: [Ci.nsIHandlerApp, Ci.nsILocalHandlerApp, Ci.nsISupports],
QueryInterface: function(iid) {
if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
}
};
var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
createInstance(Ci.nsIWebHandlerApp);
webHandler.name = "Web Handler";
webHandler.uriTemplate = "http://www.example.com/?%s";
// FIXME: these tests create and manipulate enough variables that it would
// make sense to move each test into its own scope so we don't run the risk
// of one test stomping on another's data.
//**************************************************************************//
// Test Default Properties
// Get a handler info for a MIME type that neither the application nor
// the OS knows about and make sure its properties are set to the proper
// default values.
var handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
// Make sure it's also an nsIHandlerInfo.
do_check_true(handlerInfo instanceof Ci.nsIHandlerInfo);
do_check_eq(handlerInfo.type, "nonexistent/type");
// Deprecated property, but we should still make sure it's set correctly.
do_check_eq(handlerInfo.MIMEType, "nonexistent/type");
// These properties are the ones the handler service knows how to store.
do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.saveToDisk);
do_check_eq(handlerInfo.preferredApplicationHandler, null);
do_check_eq(handlerInfo.possibleApplicationHandlers.length, 0);
do_check_true(handlerInfo.alwaysAskBeforeHandling);
// These properties are initialized to default values by the service,
// so we might as well make sure they're initialized to the right defaults.
do_check_eq(handlerInfo.description, "");
do_check_eq(handlerInfo.hasDefaultHandler, false);
do_check_eq(handlerInfo.defaultDescription, "");
// test some default protocol info properties
var haveDefaultHandlersVersion = false;
try {
// If we have a defaultHandlersVersion pref, then assume that we're in the
// firefox tree and that we'll also have default handlers.
// Bug 395131 has been filed to make this test work more generically
// by providing our own prefs for this test rather than this icky
// special casing.
rootPrefBranch.getCharPref("gecko.handlerService.defaultHandlersVersion");
haveDefaultHandlersVersion = true;
} catch (ex) {}
const kExternalWarningDefault =
"network.protocol-handler.warn-external-default";
prefSvc.setBoolPref(kExternalWarningDefault, true);
// XXX add more thorough protocol info property checking
// no OS default handler exists
var protoInfo = protoSvc.getProtocolHandlerInfo("x-moz-rheet");
do_check_eq(protoInfo.preferredAction, protoInfo.alwaysAsk);
do_check_true(protoInfo.alwaysAskBeforeHandling);
// OS default exists, injected default does not exist,
// explicit warning pref: false
const kExternalWarningPrefPrefix = "network.protocol-handler.warn-external.";
prefSvc.setBoolPref(kExternalWarningPrefPrefix + "http", false);
protoInfo = protoSvc.getProtocolHandlerInfo("http");
do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
do_check_false(protoInfo.alwaysAskBeforeHandling);
// OS default exists, injected default does not exist,
// explicit warning pref: true
prefSvc.setBoolPref(kExternalWarningPrefPrefix + "http", true);
protoInfo = protoSvc.getProtocolHandlerInfo("http");
// OS handler isn't included in possibleApplicationHandlers, so length is 0
// Once they become instances of nsILocalHandlerApp, this number will need
// to change.
do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
do_check_true(protoInfo.alwaysAskBeforeHandling);
// OS default exists, injected default exists, explicit warning pref: false
prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", false);
protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
if (haveDefaultHandlersVersion)
do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
else
do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
// Win7+ or Linux's GIO might not have a default mailto: handler
if (noMailto)
do_check_true(protoInfo.alwaysAskBeforeHandling);
else
do_check_false(protoInfo.alwaysAskBeforeHandling);
// OS default exists, injected default exists, explicit warning pref: true
prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", true);
protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
if (haveDefaultHandlersVersion) {
do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
// Win7+ or Linux's GIO may have no default mailto: handler. Otherwise
// alwaysAskBeforeHandling is expected to be false here, because although
// the pref is true, the value in RDF is false. The injected mailto handler
// carried over the default pref value, and so when we set the pref above
// to true it's ignored.
if (noMailto)
do_check_true(protoInfo.alwaysAskBeforeHandling);
else
do_check_false(protoInfo.alwaysAskBeforeHandling);
} else {
do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
do_check_true(protoInfo.alwaysAskBeforeHandling);
}
if (haveDefaultHandlersVersion) {
// Now set the value stored in RDF to true, and the pref to false, to make
// sure we still get the right value. (Basically, same thing as above but
// with the values reversed.)
prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", false);
protoInfo.alwaysAskBeforeHandling = true;
handlerSvc.store(protoInfo);
protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
do_check_true(protoInfo.alwaysAskBeforeHandling);
}
//**************************************************************************//
// Test Round-Trip Data Integrity
// Test round-trip data integrity by setting the properties of the handler
// info object to different values, telling the handler service to store the
// object, and then retrieving a new info object for the same type and making
// sure its properties are identical.
handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
handlerInfo.preferredApplicationHandler = localHandler;
handlerInfo.alwaysAskBeforeHandling = false;
handlerSvc.store(handlerInfo);
handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useHelperApp);
do_check_neq(handlerInfo.preferredApplicationHandler, null);
var preferredHandler = handlerInfo.preferredApplicationHandler;
do_check_eq(typeof preferredHandler, "object");
do_check_eq(preferredHandler.name, "Local Handler");
do_check_true(preferredHandler instanceof Ci.nsILocalHandlerApp);
preferredHandler.QueryInterface(Ci.nsILocalHandlerApp);
do_check_eq(preferredHandler.executable.path, localHandler.executable.path);
do_check_false(handlerInfo.alwaysAskBeforeHandling);
// Make sure the handler service's enumerate method lists all known handlers.
var handlerInfo2 = mimeSvc.getFromTypeAndExtension("nonexistent/type2", null);
handlerSvc.store(handlerInfo2);
var handlerTypes = ["nonexistent/type", "nonexistent/type2"];
if (haveDefaultHandlersVersion) {
handlerTypes.push("webcal");
handlerTypes.push("mailto");
handlerTypes.push("irc");
handlerTypes.push("ircs");
}
var handlers = handlerSvc.enumerate();
while (handlers.hasMoreElements()) {
var handler = handlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
do_check_neq(handlerTypes.indexOf(handler.type), -1);
handlerTypes.splice(handlerTypes.indexOf(handler.type), 1);
}
do_check_eq(handlerTypes.length, 0);
// Make sure the handler service's remove method removes a handler record.
handlerSvc.remove(handlerInfo2);
handlers = handlerSvc.enumerate();
while (handlers.hasMoreElements())
do_check_neq(handlers.getNext().QueryInterface(Ci.nsIHandlerInfo).type,
handlerInfo2.type);
// Make sure we can store and retrieve a handler info object with no preferred
// handler.
var noPreferredHandlerInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/no-preferred-handler", null);
handlerSvc.store(noPreferredHandlerInfo);
noPreferredHandlerInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/no-preferred-handler", null);
do_check_eq(noPreferredHandlerInfo.preferredApplicationHandler, null);
// Make sure that the handler service removes an existing handler record
// if we store a handler info object with no preferred handler.
var removePreferredHandlerInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/rem-preferred-handler", null);
removePreferredHandlerInfo.preferredApplicationHandler = localHandler;
handlerSvc.store(removePreferredHandlerInfo);
removePreferredHandlerInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/rem-preferred-handler", null);
removePreferredHandlerInfo.preferredApplicationHandler = null;
handlerSvc.store(removePreferredHandlerInfo);
removePreferredHandlerInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/rem-preferred-handler", null);
do_check_eq(removePreferredHandlerInfo.preferredApplicationHandler, null);
// Make sure we can store and retrieve a handler info object with possible
// handlers. We test both adding and removing handlers.
// Get a handler info and make sure it has no possible handlers.
var possibleHandlersInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 0);
// Store and re-retrieve the handler and make sure it still has no possible
// handlers.
handlerSvc.store(possibleHandlersInfo);
possibleHandlersInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 0);
// Add two handlers, store the object, re-retrieve it, and make sure it has
// two handlers.
possibleHandlersInfo.possibleApplicationHandlers.appendElement(localHandler,
false);
possibleHandlersInfo.possibleApplicationHandlers.appendElement(webHandler,
false);
handlerSvc.store(possibleHandlersInfo);
possibleHandlersInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 2);
// Figure out which is the local and which is the web handler and the index
// in the array of the local handler, which is the one we're going to remove
// to test removal of a handler.
var handler1 = possibleHandlersInfo.possibleApplicationHandlers.
queryElementAt(0, Ci.nsIHandlerApp);
var handler2 = possibleHandlersInfo.possibleApplicationHandlers.
queryElementAt(1, Ci.nsIHandlerApp);
var localPossibleHandler, webPossibleHandler, localIndex;
if (handler1 instanceof Ci.nsILocalHandlerApp)
[localPossibleHandler, webPossibleHandler, localIndex] = [handler1,
handler2,
0];
else
[localPossibleHandler, webPossibleHandler, localIndex] = [handler2,
handler1,
1];
localPossibleHandler.QueryInterface(Ci.nsILocalHandlerApp);
webPossibleHandler.QueryInterface(Ci.nsIWebHandlerApp);
// Make sure the two handlers are the ones we stored.
do_check_eq(localPossibleHandler.name, localHandler.name);
do_check_true(localPossibleHandler.equals(localHandler));
do_check_eq(webPossibleHandler.name, webHandler.name);
do_check_true(webPossibleHandler.equals(webHandler));
// Remove a handler, store the object, re-retrieve it, and make sure
// it only has one handler.
possibleHandlersInfo.possibleApplicationHandlers.removeElementAt(localIndex);
handlerSvc.store(possibleHandlersInfo);
possibleHandlersInfo =
mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 1);
// Make sure the handler is the one we didn't remove.
webPossibleHandler = possibleHandlersInfo.possibleApplicationHandlers.
queryElementAt(0, Ci.nsIWebHandlerApp);
do_check_eq(webPossibleHandler.name, webHandler.name);
do_check_true(webPossibleHandler.equals(webHandler));
//////////////////////////////////////////////////////
// handler info command line parameters and equality
var localApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
createInstance(Ci.nsILocalHandlerApp);
var handlerApp = localApp.QueryInterface(Ci.nsIHandlerApp);
do_check_true(handlerApp.equals(localApp));
localApp.executable = executable;
do_check_eq(0, localApp.parameterCount);
localApp.appendParameter("-test1");
do_check_eq(1, localApp.parameterCount);
localApp.appendParameter("-test2");
do_check_eq(2, localApp.parameterCount);
do_check_true(localApp.parameterExists("-test1"));
do_check_true(localApp.parameterExists("-test2"));
do_check_false(localApp.parameterExists("-false"));
localApp.clearParameters();
do_check_eq(0, localApp.parameterCount);
var localApp2 = Cc["@mozilla.org/uriloader/local-handler-app;1"].
createInstance(Ci.nsILocalHandlerApp);
localApp2.executable = executable;
localApp.clearParameters();
do_check_true(localApp.equals(localApp2));
// equal:
// cut -d 1 -f 2
// cut -d 1 -f 2
localApp.appendParameter("-test1");
localApp.appendParameter("-test2");
localApp.appendParameter("-test3");
localApp2.appendParameter("-test1");
localApp2.appendParameter("-test2");
localApp2.appendParameter("-test3");
do_check_true(localApp.equals(localApp2));
// not equal:
// cut -d 1 -f 2
// cut -f 1 -d 2
localApp.clearParameters();
localApp2.clearParameters();
localApp.appendParameter("-test1");
localApp.appendParameter("-test2");
localApp.appendParameter("-test3");
localApp2.appendParameter("-test2");
localApp2.appendParameter("-test1");
localApp2.appendParameter("-test3");
do_check_false(localApp2.equals(localApp));
var str;
str = localApp.getParameter(0)
do_check_eq(str, "-test1");
str = localApp.getParameter(1)
do_check_eq(str, "-test2");
str = localApp.getParameter(2)
do_check_eq(str, "-test3");
// FIXME: test round trip integrity for a protocol.
// FIXME: test round trip integrity for a handler info with a web handler.
//**************************************************************************//
// getTypeFromExtension tests
// test nonexistent extension
var lolType = handlerSvc.getTypeFromExtension("lolcat");
do_check_eq(lolType, "");
// add a handler for the extension
var lolHandler = mimeSvc.getFromTypeAndExtension("application/lolcat", null);
do_check_false(lolHandler.extensionExists("lolcat"));
lolHandler.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
lolHandler.preferredApplicationHandler = localHandler;
lolHandler.alwaysAskBeforeHandling = false;
// store the handler
do_check_false(handlerSvc.exists(lolHandler));
handlerSvc.store(lolHandler);
do_check_true(handlerSvc.exists(lolHandler));
// Get a file:// string pointing to mimeTypes.rdf
var rdfFile = HandlerServiceTest._dirSvc.get("UMimTyp", Ci.nsIFile);
var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
var rdfFileURI = fileHandler.getURLSpecFromFile(rdfFile);
// Assign a file extenstion to the handler. handlerSvc.store() doesn't
// actually store any file extensions added with setFileExtensions(), you
// have to wade into RDF muck to do so.
// Based on toolkit/mozapps/downloads/content/helperApps.js :: addExtension()
var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
var mimeSource = gRDF.GetUnicodeResource("urn:mimetype:application/lolcat");
var valueProperty = gRDF.GetUnicodeResource("http://home.netscape.com/NC-rdf#fileExtensions");
var mimeLiteral = gRDF.GetLiteral("lolcat");
var DS = gRDF.GetDataSourceBlocking(rdfFileURI);
DS.Assert(mimeSource, valueProperty, mimeLiteral, true);
// test now-existent extension
lolType = handlerSvc.getTypeFromExtension("lolcat");
do_check_eq(lolType, "application/lolcat");
// test mailcap entries with needsterminal are ignored on non-Windows non-Mac.
if (mozinfo.os != "win" && mozinfo.os != "mac") {
env.set('PERSONAL_MAILCAP', do_get_file('mailcap').path);
handlerInfo = mimeSvc.getFromTypeAndExtension("text/plain", null);
do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useSystemDefault);
do_check_eq(handlerInfo.defaultDescription, "sed");
}
}

View file

@ -0,0 +1,126 @@
/* 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/. */
// Encoded test URI to work on all platforms/independent of file encoding
const kTestURI = "http://\u65e5\u672c\u8a93.jp/";
const kExpectedURI = "http://xn--wgv71a309e.jp/";
const kOutputFile = "result.txt";
// Try several times in case the box we're running on is slow.
const kMaxCheckExistAttempts = 30; // seconds
var gCheckExistsAttempts = 0;
const tempDir = do_get_tempdir();
function checkFile() {
// This is where we expect the output
var tempFile = tempDir.clone();
tempFile.append(kOutputFile);
if (!tempFile.exists()) {
if (gCheckExistsAttempts >= kMaxCheckExistAttempts) {
do_throw("Expected File " + tempFile.path + " does not exist after " +
kMaxCheckExistAttempts + " seconds");
}
else {
++gCheckExistsAttempts;
// Wait a bit longer then try again
do_timeout(1000, checkFile);
return;
}
}
// Now read it
var fstream =
Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
var sstream =
Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
fstream.init(tempFile, -1, 0, 0);
sstream.init(fstream);
// Read the first line only as that's the one we expect WriteArguments
// to be writing the argument to.
var data = sstream.read(4096);
sstream.close();
fstream.close();
// Now remove the old file
tempFile.remove(false);
// This currently fails on Mac with an argument like -psn_0_nnnnnn
// This seems to be to do with how the executable is called, but I couldn't
// find a way around it.
// Additionally the lack of OS detection in xpcshell tests sucks, so we'll
// have to check for the argument mac gives us.
if (data.substring(0, 7) != "-psn_0_")
do_check_eq(data, kExpectedURI);
do_test_finished();
}
function run_test() {
if (mozinfo.os == "mac") {
dump("INFO | test_punycodeURIs.js | Skipping test on mac, bug 599475")
return;
}
// set up the uri to test with
var ioService =
Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
// set up the local handler object
var localHandler =
Components.classes["@mozilla.org/uriloader/local-handler-app;1"]
.createInstance(Components.interfaces.nsILocalHandlerApp);
localHandler.name = "Test Local Handler App";
// WriteArgument will just dump its arguments to a file for us.
var processDir = do_get_cwd();
var exe = processDir.clone();
exe.append("WriteArgument");
if (!exe.exists()) {
// Maybe we are on windows
exe.leafName = "WriteArgument.exe";
if (!exe.exists())
do_throw("Could not locate the WriteArgument tests executable\n");
}
var outFile = tempDir.clone();
outFile.append(kOutputFile);
// Set an environment variable for WriteArgument to pick up
var envSvc =
Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);
// The Write Argument file needs to know where its libraries are, so
// just force the path variable
// For mac
var greDir = HandlerServiceTest._dirSvc.get("GreD", Components.interfaces.nsIFile);
envSvc.set("DYLD_LIBRARY_PATH", greDir.path);
// For Linux
envSvc.set("LD_LIBRARY_PATH", greDir.path);
//XXX: handle windows
// Now tell it where we want the file.
envSvc.set("WRITE_ARGUMENT_FILE", outFile.path);
var uri = ioService.newURI(kTestURI, null, null);
// Just check we've got these matching, if we haven't there's a problem
// with ascii spec or our test case.
do_check_eq(uri.asciiSpec, kExpectedURI);
localHandler.executable = exe;
localHandler.launchWithURI(uri);
do_test_pending();
do_timeout(1000, checkFile);
}

View file

@ -0,0 +1,15 @@
[DEFAULT]
head = head_handlerService.js
tail = tail_handlerService.js
run-sequentially = Bug 912235 - Intermittent failures
[test_getTypeFromExtension_ext_to_type_mapping.js]
[test_getTypeFromExtension_with_empty_Content_Type.js]
[test_badMIMEType.js]
[test_handlerService.js]
support-files = mailcap
# Bug 676997: test consistently fails on Android
fail-if = os == "android"
[test_punycodeURIs.js]
# Bug 676997: test consistently fails on Android
fail-if = os == "android"

View file

@ -0,0 +1,231 @@
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://testing-common/MockRegistrar.js");
do_get_profile();
var DownloadListener = {
init: function () {
let obs = Services.obs;
obs.addObserver(this, "dl-done", true);
},
observe: function (subject, topic, data) {
this.onFinished(subject, topic, data);
},
QueryInterface: function (iid) {
if (iid.equals(Ci.nsIObserver) ||
iid.equals(Ci.nsISupportsWeakReference) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
DownloadListener.init();
function HelperAppDlg() { }
HelperAppDlg.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
show: function (launcher, ctx, reason, usePrivateUI) {
launcher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToFile;
launcher.launchWithApplication(null, false);
}
}
// Override the download-manager-ui to prevent anyone from trying to open
// a window.
function DownloadMgrUI() { }
DownloadMgrUI.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]),
show: function (ir, aID, reason) { },
visible: false,
getAttention: function () { }
}
function AlertsSVC() { }
AlertsSVC.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService]),
showAlertNotification: function (url, title, text, clickable, cookie, listener, name) { },
}
MockRegistrar.register("@mozilla.org/helperapplauncherdialog;1",
HelperAppDlg);
MockRegistrar.register("@mozilla.org/download-manager-ui;1",
DownloadMgrUI);
MockRegistrar.register("@mozilla.org/alerts-service;1",
AlertsSVC);
function initChildTestEnv()
{
sendCommand(' \
const Cc = Components.classes; \
const Ci = Components.interfaces; \
const Cr = Components.results; \
const Cu = Components.utils; \
Cu.import("resource://gre/modules/Services.jsm"); \
function WindowContext() { } \
\
WindowContext.prototype = { \
getInterface: function (iid) { \
if (iid.equals(Ci.nsIInterfaceRequestor) || \
iid.equals(Ci.nsIURIContentListener) || \
iid.equals(Ci.nsILoadGroup) || \
iid.equals(Ci.nsIDocumentLoader) || \
iid.equals(Ci.nsIDOMWindow)) \
return this; \
\
throw Cr.NS_ERROR_NO_INTERFACE; \
}, \
\
/* nsIURIContentListener */ \
onStartURIOpen: function (uri) { }, \
isPreferred: function (type, desiredtype) { return false; }, \
\
/* nsILoadGroup */ \
addRequest: function (request, context) { }, \
removeRequest: function (request, context, status) { } \
}; \
\
var ioservice = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);\
var uriloader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader);\
');
}
function testFinisher(endFunc) {
let ef = endFunc;
return function (file) {
ef(file);
runNextTest();
}
}
function runChildTestSet(set)
{
DownloadListener.onFinished = testFinisher(set[2]);
sendCommand('\
let uri = ioservice.newURI("http://localhost:4444' + set[0] + '", null, null); \
let channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); \
uriloader.openURI(channel, Ci.nsIURILoader.IS_CONTENT_PREFERRED, new WindowContext()); \
');
}
var httpserver = null;
var currentTest = 0;
function runNextTest()
{
if (currentTest == tests.length) {
httpserver.stop(do_test_finished);
return;
}
let set = tests[currentTest++];
runChildTestSet(set);
}
const responseBody = [0x1f, 0x8b, 0x08, 0x00, 0x16, 0x5a, 0x8a, 0x48, 0x02,
0x03, 0x2b, 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6,
0x35, 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00];
/*
* First test: a file with Content-Type application/x-gzip and Content-Encoding gzip
* should not be decoded in a round-trip
*/
function testResponse1(metadata, response) {
response.setHeader("Content-Type", "application/x-gzip", false);
response.setHeader("Content-Encoding", "gzip", false);
response.setHeader("Content-Disposition", "attachment", false);
var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
bos.setOutputStream(response.bodyOutputStream);
bos.writeByteArray(responseBody, responseBody.length);
}
function finishTest1(subject, topic, data) {
let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
do_check_true(file.path.search("test1.gz") != 0);
let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
fis.init(file, -1, -1, 0);
let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
bis.setInputStream(fis);
let str = bis.readByteArray(bis.available());
do_check_matches(str, responseBody);
}
/*
* Second test: a file with Content-Type text/html and Content-Encoding gzip
* should not be decoded in a round-trip, if its filename ends in ".gz".
* We specify a Content-disposition header to force it to be saved as a file.
*/
function testResponse2(metadata, response) {
response.setHeader("Content-Type", "text/html", false);
response.setHeader("Content-Encoding", "gzip", false);
response.setHeader("Content-Disposition", "attachment", false);
var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
bos.setOutputStream(response.bodyOutputStream);
bos.writeByteArray(responseBody, responseBody.length);
}
function finishTest2(subject, topic, data) {
let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
do_check_true(file.path.search("test2.gz") != 0);
let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
fis.init(file, -1, -1, 0);
let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
bis.setInputStream(fis);
let str = bis.readByteArray(bis.available());
do_check_matches(str, responseBody);
}
function testResponse3(metadata, response) {
response.setHeader("Content-Type", "text/html", false);
response.setHeader("Content-Encoding", "gzip", false);
response.setHeader("Content-Disposition", "attachment", false);
var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
bos.setOutputStream(response.bodyOutputStream);
bos.writeByteArray(responseBody, responseBody.length);
}
function finishTest3(subject, topic, data) {
let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
do_check_true(file.path.search("test3.txt") != 0);
let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
fis.init(file, -1, -1, 0);
let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
bis.setInputStream(fis);
let str = bis.readByteArray(bis.available());
let decodedBody = [ 116, 101, 115, 116, 10 ]; // 't','e','s','t','\n'
do_check_matches(str, decodedBody);
}
var tests = [
[ "/test1.gz", testResponse1, finishTest1 ],
[ "/test2.gz", testResponse2, finishTest2 ],
[ "/test3.txt", testResponse3, finishTest3 ],
];
function run_test() {
// do_load_child_test_harness();
httpserver = new HttpServer();
httpserver.start(4444);
do_test_pending();
initChildTestEnv();
for (let set of tests)
httpserver.registerPathHandler(set[0], set[1]);
runNextTest();
}

View file

@ -0,0 +1,8 @@
[DEFAULT]
head =
tail =
[test_encoding.js]
# Bug 676995: test hangs consistently on Android
# Bug 907732: thunderbird still uses legacy downloads manager.
skip-if = (os == "android" || buildapp == '../mail')

View file

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 nslocalhandlerappuikit_h_
#define nslocalhandlerappuikit_h_
#include "nsLocalHandlerApp.h"
class nsLocalHandlerAppUIKit final : public nsLocalHandlerApp
{
public:
nsLocalHandlerAppUIKit()
{}
~nsLocalHandlerAppUIKit()
{}
nsLocalHandlerAppUIKit(const char16_t* aName, nsIFile* aExecutable)
: nsLocalHandlerApp(aName, aExecutable)
{}
nsLocalHandlerAppUIKit(const nsAString& aName, nsIFile* aExecutable)
: nsLocalHandlerApp(aName, aExecutable)
{}
NS_IMETHOD LaunchWithURI(nsIURI* aURI, nsIInterfaceRequestor* aWindowContext);
};
#endif /* nslocalhandlerappuikit_h_ */

View file

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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/. */
#import <CoreFoundation/CoreFoundation.h>
#include "nsLocalHandlerAppUIKit.h"
#include "nsIURI.h"
NS_IMETHODIMP
nsLocalHandlerAppUIKit::LaunchWithURI(nsIURI* aURI,
nsIInterfaceRequestor* aWindowContext)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

View file

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:expandtab:shiftwidth=2:tabstop=2: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 nsMIMEInfoUIKit_h_
#define nsMIMEInfoUIKit_h_
#include "nsMIMEInfoImpl.h"
class nsMIMEInfoUIKit final : public nsMIMEInfoImpl
{
public:
explicit nsMIMEInfoUIKit(const nsACString& aMIMEType)
: nsMIMEInfoImpl(aMIMEType)
{}
nsMIMEInfoUIKit(const nsACString& aType, HandlerClass aClass)
: nsMIMEInfoImpl(aType, aClass)
{}
NS_IMETHOD LaunchWithFile(nsIFile* aFile);
protected:
virtual nsresult LoadUriInternal(nsIURI* aURI);
#ifdef DEBUG
virtual nsresult LaunchDefaultWithFile(nsIFile* aFile)
{
NS_NOTREACHED("do not call this method, use LaunchWithFile");
return NS_ERROR_UNEXPECTED;
}
#endif
static nsresult OpenApplicationWithURI(nsIFile* aApplication,
const nsCString& aURI);
};
#endif

Some files were not shown because too many files have changed in this diff Show more