mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 06:35:42 +00:00
1484 lines
45 KiB
C++
1484 lines
45 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 cin et: */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (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.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Travis Bogard <travis@netscape.com>
|
|
* Dan Rosen <dr@netscape.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "nsDocShell.h"
|
|
#include "nsWebShell.h"
|
|
#include "nsIWebBrowserChrome2.h"
|
|
#include "nsIInterfaceRequestor.h"
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
#include "nsIWebProgress.h"
|
|
#include "nsIContentViewer.h"
|
|
#include "nsIDocumentViewer.h"
|
|
#include "nsIMarkupDocumentViewer.h"
|
|
#include "nsIClipboardCommands.h"
|
|
#include "nsILinkHandler.h"
|
|
#include "nsIStreamListener.h"
|
|
#include "nsIPrompt.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsIRefreshURI.h"
|
|
#include "nsIDOMEvent.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsCRT.h"
|
|
#include "nsVoidArray.h"
|
|
#include "nsString.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "prprf.h"
|
|
#include "nsIPluginHost.h"
|
|
#include "nsplugin.h"
|
|
#include "nsIPluginManager.h"
|
|
#include "nsCDefaultURIFixup.h"
|
|
#include "nsIContent.h"
|
|
#include "prlog.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIWebShellServices.h"
|
|
#include "nsIGlobalHistory.h"
|
|
#include "prmem.h"
|
|
#include "prthread.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsDOMError.h"
|
|
#include "nsIDOMRange.h"
|
|
#include "nsIURIContentListener.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMHTMLAnchorElement.h"
|
|
#include "nsIBaseWindow.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsIDocShellTreeItem.h"
|
|
#include "nsIDocShellTreeNode.h"
|
|
#include "nsIDocShellTreeOwner.h"
|
|
#include "nsCURILoader.h"
|
|
#include "nsURILoader.h"
|
|
#include "nsIDOMWindowInternal.h"
|
|
#include "nsEscape.h"
|
|
#include "nsIPlatformCharset.h"
|
|
#include "nsICharsetConverterManager.h"
|
|
#include "nsISocketTransportService.h"
|
|
#include "nsTextFormatter.h"
|
|
#include "nsPIDOMWindow.h"
|
|
#include "nsPICommandUpdater.h"
|
|
#include "nsIController.h"
|
|
#include "nsIFocusController.h"
|
|
#include "nsGUIEvent.h"
|
|
#include "nsISHistoryInternal.h"
|
|
|
|
#include "nsIHttpChannel.h"
|
|
#include "nsIHttpChannelInternal.h"
|
|
#include "nsIUploadChannel.h"
|
|
#include "nsISeekableStream.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "nsILocaleService.h"
|
|
#include "nsIStringBundle.h"
|
|
|
|
#include "nsICachingChannel.h"
|
|
|
|
#include "nsIDocument.h"
|
|
#include "nsITextToSubURI.h"
|
|
|
|
#include "nsIExternalProtocolService.h"
|
|
#include "nsCExternalHandlerService.h"
|
|
|
|
#include "nsIIDNService.h"
|
|
|
|
#include "nsIPrefBranch.h"
|
|
#include "nsIPrefService.h"
|
|
#include "nsITimer.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsContentPolicyUtils.h"
|
|
|
|
#include "nsIURIClassifier.h"
|
|
|
|
#ifdef NS_DEBUG
|
|
/**
|
|
* Note: the log module is created during initialization which
|
|
* means that you cannot perform logging before then.
|
|
*/
|
|
static PRLogModuleInfo* gLogModule = PR_NewLogModule("webshell");
|
|
#endif
|
|
|
|
#define WEB_TRACE_CALLS 0x1
|
|
#define WEB_TRACE_HISTORY 0x2
|
|
|
|
#define WEB_LOG_TEST(_lm,_bit) (PRIntn((_lm)->level) & (_bit))
|
|
|
|
#ifdef NS_DEBUG
|
|
#define WEB_TRACE(_bit,_args) \
|
|
PR_BEGIN_MACRO \
|
|
if (WEB_LOG_TEST(gLogModule,_bit)) { \
|
|
PR_LogPrint _args; \
|
|
} \
|
|
PR_END_MACRO
|
|
#else
|
|
#define WEB_TRACE(_bit,_args)
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define PREF_PINGS_ENABLED "browser.send_pings"
|
|
#define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
|
|
#define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
|
|
|
|
// Check prefs to see if pings are enabled and if so what restrictions might
|
|
// be applied.
|
|
//
|
|
// @param maxPerLink
|
|
// This parameter returns the number of pings that are allowed per link click
|
|
//
|
|
// @param requireSameHost
|
|
// This parameter returns PR_TRUE if pings are restricted to the same host as
|
|
// the document in which the click occurs. If the same host restriction is
|
|
// imposed, then we still allow for pings to cross over to different
|
|
// protocols and ports for flexibility and because it is not possible to send
|
|
// a ping via FTP.
|
|
//
|
|
// @returns
|
|
// PR_TRUE if pings are enabled and PR_FALSE otherwise.
|
|
//
|
|
static PRBool
|
|
PingsEnabled(PRInt32 *maxPerLink, PRBool *requireSameHost)
|
|
{
|
|
PRBool allow = PR_FALSE;
|
|
|
|
*maxPerLink = 1;
|
|
*requireSameHost = PR_TRUE;
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefs =
|
|
do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
if (prefs) {
|
|
PRBool val;
|
|
if (NS_SUCCEEDED(prefs->GetBoolPref(PREF_PINGS_ENABLED, &val)))
|
|
allow = val;
|
|
if (allow) {
|
|
prefs->GetIntPref(PREF_PINGS_MAX_PER_LINK, maxPerLink);
|
|
prefs->GetBoolPref(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
|
|
}
|
|
}
|
|
|
|
return allow;
|
|
}
|
|
|
|
static PRBool
|
|
CheckPingURI(nsIURI* uri, nsIContent* content)
|
|
{
|
|
if (!uri)
|
|
return PR_FALSE;
|
|
|
|
// Check with nsIScriptSecurityManager
|
|
nsCOMPtr<nsIScriptSecurityManager> ssmgr =
|
|
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
|
NS_ENSURE_TRUE(ssmgr, PR_FALSE);
|
|
|
|
nsresult rv =
|
|
ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
|
|
nsIScriptSecurityManager::STANDARD);
|
|
if (NS_FAILED(rv)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Ignore non-HTTP(S)
|
|
PRBool match;
|
|
if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) &&
|
|
(NS_FAILED(uri->SchemeIs("https", &match)) || !match)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Check with contentpolicy
|
|
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
|
|
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING,
|
|
uri,
|
|
content->NodePrincipal(),
|
|
content,
|
|
EmptyCString(), // mime hint
|
|
nsnull, //extra
|
|
&shouldLoad);
|
|
return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
|
|
}
|
|
|
|
typedef void (* ForEachPingCallback)(void *closure, nsIContent *content,
|
|
nsIURI *uri, nsIIOService *ios);
|
|
|
|
static void
|
|
ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure)
|
|
{
|
|
// NOTE: Using nsIDOMNSHTMLAnchorElement2::GetPing isn't really worth it here
|
|
// since we'd still need to parse the resulting string. Instead, we
|
|
// just parse the raw attribute. It might be nice if the content node
|
|
// implemented an interface that exposed an enumeration of nsIURIs.
|
|
|
|
// Make sure we are dealing with either an <A> or <AREA> element in the HTML
|
|
// or XHTML namespace.
|
|
if (!content->IsNodeOfType(nsINode::eHTML))
|
|
return;
|
|
nsIAtom *nameAtom = content->Tag();
|
|
if (!nameAtom->EqualsUTF8(NS_LITERAL_CSTRING("a")) &&
|
|
!nameAtom->EqualsUTF8(NS_LITERAL_CSTRING("area")))
|
|
return;
|
|
|
|
nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
|
|
if (!pingAtom)
|
|
return;
|
|
|
|
nsAutoString value;
|
|
content->GetAttr(kNameSpaceID_None, pingAtom, value);
|
|
if (value.IsEmpty())
|
|
return;
|
|
|
|
nsCOMPtr<nsIIOService> ios = do_GetIOService();
|
|
if (!ios)
|
|
return;
|
|
|
|
nsIDocument *doc = content->GetOwnerDoc();
|
|
if (!doc)
|
|
return;
|
|
|
|
// value contains relative URIs split on spaces (U+0020)
|
|
const PRUnichar *start = value.BeginReading();
|
|
const PRUnichar *end = value.EndReading();
|
|
const PRUnichar *iter = start;
|
|
for (;;) {
|
|
if (iter < end && *iter != ' ') {
|
|
++iter;
|
|
} else { // iter is pointing at either end or a space
|
|
while (*start == ' ' && start < iter)
|
|
++start;
|
|
if (iter != start) {
|
|
nsCOMPtr<nsIURI> uri, baseURI = content->GetBaseURI();
|
|
ios->NewURI(NS_ConvertUTF16toUTF8(Substring(start, iter)),
|
|
doc->GetDocumentCharacterSet().get(),
|
|
baseURI, getter_AddRefs(uri));
|
|
if (CheckPingURI(uri, content)) {
|
|
callback(closure, content, uri, ios);
|
|
}
|
|
}
|
|
start = iter = iter + 1;
|
|
if (iter >= end)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// We wait this many milliseconds before killing the ping channel...
|
|
#define PING_TIMEOUT 10000
|
|
|
|
static void
|
|
OnPingTimeout(nsITimer *timer, void *closure)
|
|
{
|
|
nsILoadGroup *loadGroup = static_cast<nsILoadGroup *>(closure);
|
|
loadGroup->Cancel(NS_ERROR_ABORT);
|
|
loadGroup->Release();
|
|
}
|
|
|
|
// Check to see if two URIs have the same host or not
|
|
static PRBool
|
|
IsSameHost(nsIURI *uri1, nsIURI *uri2)
|
|
{
|
|
nsCAutoString host1, host2;
|
|
uri1->GetAsciiHost(host1);
|
|
uri2->GetAsciiHost(host2);
|
|
return host1.Equals(host2);
|
|
}
|
|
|
|
class nsPingListener : public nsIStreamListener
|
|
, public nsIInterfaceRequestor
|
|
, public nsIChannelEventSink
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIREQUESTOBSERVER
|
|
NS_DECL_NSISTREAMLISTENER
|
|
NS_DECL_NSIINTERFACEREQUESTOR
|
|
NS_DECL_NSICHANNELEVENTSINK
|
|
|
|
nsPingListener(PRBool requireSameHost, nsIContent* content)
|
|
: mRequireSameHost(requireSameHost),
|
|
mContent(content)
|
|
{}
|
|
|
|
private:
|
|
PRBool mRequireSameHost;
|
|
nsCOMPtr<nsIContent> mContent;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS4(nsPingListener, nsIStreamListener, nsIRequestObserver,
|
|
nsIInterfaceRequestor, nsIChannelEventSink)
|
|
|
|
NS_IMETHODIMP
|
|
nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context,
|
|
nsIInputStream *stream, PRUint32 offset,
|
|
PRUint32 count)
|
|
{
|
|
PRUint32 result;
|
|
return stream->ReadSegments(NS_DiscardSegment, nsnull, count, &result);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context,
|
|
nsresult status)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPingListener::GetInterface(const nsIID &iid, void **result)
|
|
{
|
|
if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
|
|
NS_ADDREF_THIS();
|
|
*result = (nsIChannelEventSink *) this;
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_ERROR_NO_INTERFACE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPingListener::OnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
|
|
PRUint32 flags)
|
|
{
|
|
nsCOMPtr<nsIURI> newURI;
|
|
newChan->GetURI(getter_AddRefs(newURI));
|
|
|
|
if (!CheckPingURI(newURI, mContent))
|
|
return NS_ERROR_ABORT;
|
|
|
|
if (!mRequireSameHost)
|
|
return NS_OK;
|
|
|
|
// XXXbz should this be using something more like the nsContentUtils
|
|
// same-origin checker?
|
|
nsCOMPtr<nsIURI> oldURI;
|
|
oldChan->GetURI(getter_AddRefs(oldURI));
|
|
NS_ENSURE_STATE(oldURI && newURI);
|
|
|
|
if (!IsSameHost(oldURI, newURI))
|
|
return NS_ERROR_ABORT;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
struct SendPingInfo {
|
|
PRInt32 numPings;
|
|
PRInt32 maxPings;
|
|
PRBool requireSameHost;
|
|
nsIURI *referrer;
|
|
};
|
|
|
|
static void
|
|
SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios)
|
|
{
|
|
SendPingInfo *info = static_cast<SendPingInfo *>(closure);
|
|
if (info->numPings >= info->maxPings)
|
|
return;
|
|
|
|
if (info->requireSameHost) {
|
|
// Make sure the referrer and the given uri share the same origin. We
|
|
// only require the same hostname. The scheme and port may differ.
|
|
if (!IsSameHost(uri, info->referrer))
|
|
return;
|
|
}
|
|
|
|
nsIDocument *doc = content->GetOwnerDoc();
|
|
if (!doc)
|
|
return;
|
|
|
|
nsCOMPtr<nsIChannel> chan;
|
|
ios->NewChannelFromURI(uri, getter_AddRefs(chan));
|
|
if (!chan)
|
|
return;
|
|
|
|
// Don't bother caching the result of this URI load.
|
|
chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING);
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
|
|
if (!httpChan)
|
|
return;
|
|
|
|
// This is needed in order for 3rd-party cookie blocking to work.
|
|
nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
|
|
if (httpInternal)
|
|
httpInternal->SetDocumentURI(doc->GetDocumentURI());
|
|
|
|
if (info->referrer)
|
|
httpChan->SetReferrer(info->referrer);
|
|
|
|
httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
|
|
|
|
// Remove extraneous request headers (to reduce request size)
|
|
httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
|
|
EmptyCString(), PR_FALSE);
|
|
httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
|
|
EmptyCString(), PR_FALSE);
|
|
httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-charset"),
|
|
EmptyCString(), PR_FALSE);
|
|
httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
|
|
EmptyCString(), PR_FALSE);
|
|
|
|
nsCOMPtr<nsIUploadChannel> uploadChan = do_QueryInterface(httpChan);
|
|
if (!uploadChan)
|
|
return;
|
|
|
|
// To avoid sending an unnecessary Content-Type header, we encode the
|
|
// closing portion of the headers in the POST body.
|
|
NS_NAMED_LITERAL_CSTRING(uploadData, "Content-Length: 0\r\n\r\n");
|
|
|
|
nsCOMPtr<nsIInputStream> uploadStream;
|
|
NS_NewPostDataStream(getter_AddRefs(uploadStream), PR_FALSE,
|
|
uploadData, 0);
|
|
if (!uploadStream)
|
|
return;
|
|
|
|
uploadChan->SetUploadStream(uploadStream, EmptyCString(), -1);
|
|
|
|
// The channel needs to have a loadgroup associated with it, so that we can
|
|
// cancel the channel and any redirected channels it may create.
|
|
nsCOMPtr<nsILoadGroup> loadGroup =
|
|
do_CreateInstance(NS_LOADGROUP_CONTRACTID);
|
|
if (!loadGroup)
|
|
return;
|
|
chan->SetLoadGroup(loadGroup);
|
|
|
|
// Construct a listener that merely discards any response. If successful at
|
|
// opening the channel, then it is not necessary to hold a reference to the
|
|
// channel. The networking subsystem will take care of that for us.
|
|
nsCOMPtr<nsIStreamListener> listener =
|
|
new nsPingListener(info->requireSameHost, content);
|
|
if (!listener)
|
|
return;
|
|
|
|
// Observe redirects as well:
|
|
nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(listener);
|
|
NS_ASSERTION(callbacks, "oops");
|
|
loadGroup->SetNotificationCallbacks(callbacks);
|
|
|
|
chan->AsyncOpen(listener, nsnull);
|
|
|
|
// Even if AsyncOpen failed, we still count this as a successful ping. It's
|
|
// possible that AsyncOpen may have failed after triggering some background
|
|
// process that may have written something to the network.
|
|
info->numPings++;
|
|
|
|
// Prevent ping requests from stalling and never being garbage collected...
|
|
nsCOMPtr<nsITimer> timer =
|
|
do_CreateInstance(NS_TIMER_CONTRACTID);
|
|
if (timer) {
|
|
nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, loadGroup,
|
|
PING_TIMEOUT,
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// When the timer expires, the callback function will release this
|
|
// reference to the loadgroup.
|
|
static_cast<nsILoadGroup *>(loadGroup.get())->AddRef();
|
|
loadGroup = 0;
|
|
}
|
|
}
|
|
|
|
// If we failed to setup the timer, then we should just cancel the channel
|
|
// because we won't be able to ensure that it goes away in a timely manner.
|
|
if (loadGroup)
|
|
chan->Cancel(NS_ERROR_ABORT);
|
|
}
|
|
|
|
// Spec: http://whatwg.org/specs/web-apps/current-work/#ping
|
|
static void
|
|
DispatchPings(nsIContent *content, nsIURI *referrer)
|
|
{
|
|
SendPingInfo info;
|
|
|
|
if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
|
|
return;
|
|
if (info.maxPings == 0)
|
|
return;
|
|
|
|
info.numPings = 0;
|
|
info.referrer = referrer;
|
|
|
|
ForEachPing(content, SendPing, &info);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Note: operator new zeros our memory
|
|
nsWebShell::nsWebShell() : nsDocShell()
|
|
{
|
|
#ifdef DEBUG
|
|
// We're counting the number of |nsWebShells| to help find leaks
|
|
++gNumberOfWebShells;
|
|
#endif
|
|
#ifdef DEBUG
|
|
printf("++WEBSHELL %p == %ld\n", (void*) this, gNumberOfWebShells);
|
|
#endif
|
|
|
|
InitFrameData();
|
|
mItemType = typeContent;
|
|
mCharsetReloadState = eCharsetReloadInit;
|
|
}
|
|
|
|
nsWebShell::~nsWebShell()
|
|
{
|
|
Destroy();
|
|
|
|
++mRefCnt; // following releases can cause this destructor to be called
|
|
// recursively if the refcount is allowed to remain 0
|
|
|
|
mContentViewer=nsnull;
|
|
|
|
InitFrameData();
|
|
|
|
#ifdef DEBUG
|
|
// We're counting the number of |nsWebShells| to help find leaks
|
|
--gNumberOfWebShells;
|
|
#endif
|
|
#ifdef DEBUG
|
|
printf("--WEBSHELL %p == %ld\n", (void*) this, gNumberOfWebShells);
|
|
#endif
|
|
}
|
|
|
|
void nsWebShell::InitFrameData()
|
|
{
|
|
SetMarginWidth(-1);
|
|
SetMarginHeight(-1);
|
|
}
|
|
|
|
nsresult
|
|
nsWebShell::EnsureCommandHandler()
|
|
{
|
|
if (!mCommandManager)
|
|
{
|
|
mCommandManager = do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
|
|
if (!mCommandManager) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
nsCOMPtr<nsPICommandUpdater> commandUpdater = do_QueryInterface(mCommandManager);
|
|
if (!commandUpdater) return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(static_cast<nsIInterfaceRequestor *>(this));
|
|
#ifdef DEBUG
|
|
nsresult rv =
|
|
#endif
|
|
commandUpdater->Init(domWindow);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Initting command manager failed");
|
|
}
|
|
|
|
return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsWebShell, nsDocShell)
|
|
NS_IMPL_RELEASE_INHERITED(nsWebShell, nsDocShell)
|
|
|
|
NS_INTERFACE_MAP_BEGIN(nsWebShell)
|
|
NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
|
|
NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
|
|
NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
|
|
NS_INTERFACE_MAP_END_INHERITING(nsDocShell)
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::GetInterface(const nsIID &aIID, void** aInstancePtr)
|
|
{
|
|
NS_PRECONDITION(aInstancePtr, "null out param");
|
|
|
|
*aInstancePtr = nsnull;
|
|
|
|
if(aIID.Equals(NS_GET_IID(nsICommandManager)))
|
|
{
|
|
NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
|
|
*aInstancePtr = mCommandManager;
|
|
NS_ADDREF((nsISupports*) *aInstancePtr);
|
|
return NS_OK;
|
|
}
|
|
|
|
return nsDocShell::GetInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
nsEventStatus
|
|
nsWebShell::HandleEvent(nsGUIEvent *aEvent)
|
|
{
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// Web Shell Services API
|
|
|
|
//This functions is only called when a new charset is detected in loading a document.
|
|
//Its name should be changed to "CharsetReloadDocument"
|
|
NS_IMETHODIMP
|
|
nsWebShell::ReloadDocument(const char* aCharset,
|
|
PRInt32 aSource)
|
|
{
|
|
|
|
// XXX hack. kee the aCharset and aSource wait to pick it up
|
|
nsCOMPtr<nsIContentViewer> cv;
|
|
NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
|
|
if (cv)
|
|
{
|
|
nsCOMPtr<nsIMarkupDocumentViewer> muDV = do_QueryInterface(cv);
|
|
if (muDV)
|
|
{
|
|
PRInt32 hint;
|
|
muDV->GetHintCharacterSetSource(&hint);
|
|
if( aSource > hint )
|
|
{
|
|
muDV->SetHintCharacterSet(nsDependentCString(aCharset));
|
|
muDV->SetHintCharacterSetSource(aSource);
|
|
if(eCharsetReloadRequested != mCharsetReloadState)
|
|
{
|
|
mCharsetReloadState = eCharsetReloadRequested;
|
|
return Reload(LOAD_FLAGS_CHARSET_CHANGE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//return failer if this request is not accepted due to mCharsetReloadState
|
|
return NS_ERROR_WEBSHELL_REQUEST_REJECTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::StopDocumentLoad(void)
|
|
{
|
|
if(eCharsetReloadRequested != mCharsetReloadState)
|
|
{
|
|
Stop(nsIWebNavigation::STOP_ALL);
|
|
return NS_OK;
|
|
}
|
|
//return failer if this request is not accepted due to mCharsetReloadState
|
|
return NS_ERROR_WEBSHELL_REQUEST_REJECTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::SetRendering(PRBool aRender)
|
|
{
|
|
if(eCharsetReloadRequested != mCharsetReloadState)
|
|
{
|
|
if (mContentViewer) {
|
|
mContentViewer->SetEnableRendering(aRender);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
//return failer if this request is not accepted due to mCharsetReloadState
|
|
return NS_ERROR_WEBSHELL_REQUEST_REJECTED;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// WebShell link handling
|
|
|
|
class OnLinkClickEvent : public nsRunnable {
|
|
public:
|
|
OnLinkClickEvent(nsWebShell* aHandler, nsIContent* aContent,
|
|
nsIURI* aURI,
|
|
const PRUnichar* aTargetSpec,
|
|
nsIInputStream* aPostDataStream = 0,
|
|
nsIInputStream* aHeadersDataStream = 0);
|
|
|
|
NS_IMETHOD Run() {
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
|
|
nsAutoPopupStatePusher popupStatePusher(window, mPopupState);
|
|
|
|
mHandler->OnLinkClickSync(mContent, mURI,
|
|
mTargetSpec.get(), mPostDataStream,
|
|
mHeadersDataStream,
|
|
nsnull, nsnull);
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsRefPtr<nsWebShell> mHandler;
|
|
nsCOMPtr<nsIURI> mURI;
|
|
nsString mTargetSpec;
|
|
nsCOMPtr<nsIInputStream> mPostDataStream;
|
|
nsCOMPtr<nsIInputStream> mHeadersDataStream;
|
|
nsCOMPtr<nsIContent> mContent;
|
|
PopupControlState mPopupState;
|
|
};
|
|
|
|
OnLinkClickEvent::OnLinkClickEvent(nsWebShell* aHandler,
|
|
nsIContent *aContent,
|
|
nsIURI* aURI,
|
|
const PRUnichar* aTargetSpec,
|
|
nsIInputStream* aPostDataStream,
|
|
nsIInputStream* aHeadersDataStream)
|
|
: mHandler(aHandler)
|
|
, mURI(aURI)
|
|
, mTargetSpec(aTargetSpec)
|
|
, mPostDataStream(aPostDataStream)
|
|
, mHeadersDataStream(aHeadersDataStream)
|
|
, mContent(aContent)
|
|
{
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
|
|
|
|
mPopupState = window->GetPopupControlState();
|
|
}
|
|
|
|
//----------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::OnLinkClick(nsIContent* aContent,
|
|
nsIURI* aURI,
|
|
const PRUnichar* aTargetSpec,
|
|
nsIInputStream* aPostDataStream,
|
|
nsIInputStream* aHeadersDataStream)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
|
|
|
if (!IsOKToLoadURI(aURI)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aContent->IsEditable()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIRunnable> ev =
|
|
new OnLinkClickEvent(this, aContent, aURI, aTargetSpec,
|
|
aPostDataStream, aHeadersDataStream);
|
|
return NS_DispatchToCurrentThread(ev);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::OnLinkClickSync(nsIContent *aContent,
|
|
nsIURI* aURI,
|
|
const PRUnichar* aTargetSpec,
|
|
nsIInputStream* aPostDataStream,
|
|
nsIInputStream* aHeadersDataStream,
|
|
nsIDocShell** aDocShell,
|
|
nsIRequest** aRequest)
|
|
{
|
|
// Initialize the DocShell / Request
|
|
if (aDocShell) {
|
|
*aDocShell = nsnull;
|
|
}
|
|
if (aRequest) {
|
|
*aRequest = nsnull;
|
|
}
|
|
|
|
if (!IsOKToLoadURI(aURI)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aContent->IsEditable()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
{
|
|
// defer to an external protocol handler if necessary...
|
|
nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
|
|
if (extProtService) {
|
|
nsCAutoString scheme;
|
|
aURI->GetScheme(scheme);
|
|
if (!scheme.IsEmpty()) {
|
|
// if the URL scheme does not correspond to an exposed protocol, then we
|
|
// need to hand this link click over to the external protocol handler.
|
|
PRBool isExposed;
|
|
nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
|
|
if (NS_SUCCEEDED(rv) && !isExposed) {
|
|
return extProtService->LoadURI(aURI, this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
|
|
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
|
|
|
|
PRBool inherit;
|
|
nsresult rv = URIInheritsSecurityContext(aURI, &inherit);
|
|
if (NS_FAILED(rv) || inherit) {
|
|
nsCOMPtr<nsIDocument> sourceDoc = aContent->GetDocument();
|
|
|
|
if (!sourceDoc) {
|
|
// The source is in a 'zombie' document, or not part of a
|
|
// document any more. Don't let it perform loads in this docshell.
|
|
// XXXbz why only for the inherit case?
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
GetPresShell(getter_AddRefs(presShell));
|
|
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
|
|
|
if (presShell->GetDocument() != sourceDoc) {
|
|
// The source is not in the current document, don't let it load anything
|
|
// that would inherit the principals of the current document.
|
|
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// Get the owner document of the link that was clicked, this will be
|
|
// the document that the link is in, or the last document that the
|
|
// link was in. From that document, we'll get the URI to use as the
|
|
// referer, since the current URI in this webshell/docshell may be a
|
|
// new document that we're in the process of loading.
|
|
nsCOMPtr<nsIDOMDocument> refererOwnerDoc;
|
|
node->GetOwnerDocument(getter_AddRefs(refererOwnerDoc));
|
|
|
|
nsCOMPtr<nsIDocument> refererDoc(do_QueryInterface(refererOwnerDoc));
|
|
NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
|
|
|
|
nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
|
|
|
|
// referer could be null here in some odd cases, but that's ok,
|
|
// we'll just load the link w/o sending a referer in those cases.
|
|
|
|
nsAutoString target(aTargetSpec);
|
|
|
|
// If this is an anchor element, grab its type property to use as a hint
|
|
nsAutoString typeHint;
|
|
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
|
|
if (anchor) {
|
|
anchor->GetType(typeHint);
|
|
}
|
|
|
|
rv = InternalLoad(aURI, // New URI
|
|
referer, // Referer URI
|
|
nsnull, // No onwer
|
|
INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner from document
|
|
target.get(), // Window target
|
|
NS_LossyConvertUTF16toASCII(typeHint).get(),
|
|
aPostDataStream, // Post data stream
|
|
aHeadersDataStream, // Headers stream
|
|
LOAD_LINK, // Load type
|
|
nsnull, // No SHEntry
|
|
PR_TRUE, // first party site
|
|
aDocShell, // DocShell out-param
|
|
aRequest); // Request out-param
|
|
if (NS_SUCCEEDED(rv)) {
|
|
DispatchPings(aContent, referer);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::OnOverLink(nsIContent* aContent,
|
|
nsIURI* aURI,
|
|
const PRUnichar* aTargetSpec)
|
|
{
|
|
if (aContent->IsEditable()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIWebBrowserChrome> browserChrome;
|
|
if (!browserChrome2) {
|
|
browserChrome = do_GetInterface(mTreeOwner);
|
|
if (!browserChrome)
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsITextToSubURI> textToSubURI =
|
|
do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// use url origin charset to unescape the URL
|
|
nsCAutoString charset;
|
|
rv = aURI->GetOriginCharset(charset);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCAutoString spec;
|
|
rv = aURI->GetSpec(spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString uStr;
|
|
rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (browserChrome2) {
|
|
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
|
|
rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
|
|
uStr, element);
|
|
} else {
|
|
rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::OnLeaveLink()
|
|
{
|
|
nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
if (browserChrome) {
|
|
rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
|
|
EmptyString().get());
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::GetLinkState(nsIURI* aLinkURI, nsLinkState& aState)
|
|
{
|
|
if (!aLinkURI) {
|
|
// No uri means not a link
|
|
aState = eLinkState_NotLink;
|
|
return NS_OK;
|
|
}
|
|
|
|
aState = eLinkState_Unvisited;
|
|
|
|
// no history, leave state unchanged
|
|
if (!mGlobalHistory)
|
|
return NS_OK;
|
|
|
|
PRBool isVisited;
|
|
NS_ENSURE_SUCCESS(mGlobalHistory->IsVisited(aLinkURI, &isVisited),
|
|
NS_ERROR_FAILURE);
|
|
if (isVisited)
|
|
aState = eLinkState_Visited;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
nsresult nsWebShell::EndPageLoad(nsIWebProgress *aProgress,
|
|
nsIChannel* channel,
|
|
nsresult aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if(!channel)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsCOMPtr<nsIURI> url;
|
|
rv = channel->GetURI(getter_AddRefs(url));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// clean up reload state for meta charset
|
|
if(eCharsetReloadRequested == mCharsetReloadState)
|
|
mCharsetReloadState = eCharsetReloadStopOrigional;
|
|
else
|
|
mCharsetReloadState = eCharsetReloadInit;
|
|
|
|
// Save a pointer to the currently-loading history entry.
|
|
// nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
|
|
// entry further down in this method.
|
|
nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
|
|
|
|
//
|
|
// one of many safeguards that prevent death and destruction if
|
|
// someone is so very very rude as to bring this window down
|
|
// during this load handler.
|
|
//
|
|
nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
|
|
nsDocShell::EndPageLoad(aProgress, channel, aStatus);
|
|
|
|
// Test if this is the top frame or a subframe
|
|
PRBool isTopFrame = PR_TRUE;
|
|
nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
|
|
rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
|
|
if (NS_SUCCEEDED(rv) && targetParentTreeItem)
|
|
{
|
|
isTopFrame = PR_FALSE;
|
|
}
|
|
|
|
//
|
|
// If the page load failed, then deal with the error condition...
|
|
// Errors are handled as follows:
|
|
// 1. Check to see if it's a file not found error or bad content
|
|
// encoding error.
|
|
// 2. Send the URI to a keyword server (if enabled)
|
|
// 3. If the error was DNS failure, then add www and .com to the URI
|
|
// (if appropriate).
|
|
// 4. Throw an error dialog box...
|
|
//
|
|
|
|
if (url && NS_FAILED(aStatus))
|
|
{
|
|
if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
|
|
aStatus == NS_ERROR_INVALID_CONTENT_ENCODING)
|
|
{
|
|
DisplayLoadError(aStatus, url, nsnull, channel);
|
|
return NS_OK;
|
|
}
|
|
|
|
if (sURIFixup)
|
|
{
|
|
//
|
|
// Try and make an alternative URI from the old one
|
|
//
|
|
nsCOMPtr<nsIURI> newURI;
|
|
|
|
nsCAutoString oldSpec;
|
|
url->GetSpec(oldSpec);
|
|
|
|
//
|
|
// First try keyword fixup
|
|
//
|
|
if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup)
|
|
{
|
|
PRBool keywordsEnabled = PR_FALSE;
|
|
|
|
if (mPrefs && NS_FAILED(mPrefs->GetBoolPref("keyword.enabled", &keywordsEnabled)))
|
|
keywordsEnabled = PR_FALSE;
|
|
|
|
nsCAutoString host;
|
|
url->GetHost(host);
|
|
|
|
nsCAutoString scheme;
|
|
url->GetScheme(scheme);
|
|
|
|
PRInt32 dotLoc = host.FindChar('.');
|
|
|
|
// we should only perform a keyword search under the following conditions:
|
|
// (0) Pref keyword.enabled is true
|
|
// (1) the url scheme is http (or https)
|
|
// (2) the url does not have a protocol scheme
|
|
// If we don't enforce such a policy, then we end up doing keyword searchs on urls
|
|
// we don't intend like imap, file, mailbox, etc. This could lead to a security
|
|
// problem where we send data to the keyword server that we shouldn't be.
|
|
// Someone needs to clean up keywords in general so we can determine on a per url basis
|
|
// if we want keywords enabled...this is just a bandaid...
|
|
if (keywordsEnabled && !scheme.IsEmpty() &&
|
|
(scheme.Find("http") != 0)) {
|
|
keywordsEnabled = PR_FALSE;
|
|
}
|
|
|
|
if(keywordsEnabled && (kNotFound == dotLoc)) {
|
|
// only send non-qualified hosts to the keyword server
|
|
//
|
|
// If this string was passed through nsStandardURL by chance, then it
|
|
// may have been converted from UTF-8 to ACE, which would result in a
|
|
// completely bogus keyword query. Here we try to recover the
|
|
// original Unicode value, but this is not 100% correct since the
|
|
// value may have been normalized per the IDN normalization rules.
|
|
//
|
|
// Since we don't have access to the exact original string that was
|
|
// entered by the user, this will just have to do.
|
|
//
|
|
PRBool isACE;
|
|
nsCAutoString utf8Host;
|
|
nsCOMPtr<nsIIDNService> idnSrv =
|
|
do_GetService(NS_IDNSERVICE_CONTRACTID);
|
|
if (idnSrv &&
|
|
NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
|
|
NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host)))
|
|
sURIFixup->KeywordToURI(utf8Host, getter_AddRefs(newURI));
|
|
else
|
|
sURIFixup->KeywordToURI(host, getter_AddRefs(newURI));
|
|
} // end keywordsEnabled
|
|
}
|
|
|
|
//
|
|
// Now try change the address, e.g. turn http://foo into http://www.foo.com
|
|
//
|
|
if (aStatus == NS_ERROR_UNKNOWN_HOST ||
|
|
aStatus == NS_ERROR_NET_RESET)
|
|
{
|
|
PRBool doCreateAlternate = PR_TRUE;
|
|
|
|
// Skip fixup for anything except a normal document load operation on
|
|
// the topframe.
|
|
|
|
if (mLoadType != LOAD_NORMAL || !isTopFrame)
|
|
{
|
|
doCreateAlternate = PR_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Test if keyword lookup produced a new URI or not
|
|
if (newURI)
|
|
{
|
|
PRBool sameURI = PR_FALSE;
|
|
url->Equals(newURI, &sameURI);
|
|
if (!sameURI)
|
|
{
|
|
// Keyword lookup made a new URI so no need to try an
|
|
// alternate one.
|
|
doCreateAlternate = PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (doCreateAlternate)
|
|
{
|
|
newURI = nsnull;
|
|
sURIFixup->CreateFixupURI(oldSpec,
|
|
nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI, getter_AddRefs(newURI));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Did we make a new URI that is different to the old one? If so load it.
|
|
//
|
|
if (newURI)
|
|
{
|
|
// Make sure the new URI is different from the old one, otherwise
|
|
// there's little point trying to load it again.
|
|
PRBool sameURI = PR_FALSE;
|
|
url->Equals(newURI, &sameURI);
|
|
if (!sameURI)
|
|
{
|
|
nsCAutoString newSpec;
|
|
newURI->GetSpec(newSpec);
|
|
NS_ConvertUTF8toUTF16 newSpecW(newSpec);
|
|
|
|
return LoadURI(newSpecW.get(), // URI string
|
|
LOAD_FLAGS_NONE, // Load flags
|
|
nsnull, // Referring URI
|
|
nsnull, // Post data stream
|
|
nsnull); // Headers stream
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Well, fixup didn't work :-(
|
|
// It is time to throw an error dialog box, and be done with it...
|
|
//
|
|
|
|
// Errors to be shown only on top-level frames
|
|
if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
|
|
aStatus == NS_ERROR_CONNECTION_REFUSED ||
|
|
aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
|
|
aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
|
|
(isTopFrame || mUseErrorPages)) {
|
|
DisplayLoadError(aStatus, url, nsnull, channel);
|
|
}
|
|
// Errors to be shown for any frame
|
|
else if (aStatus == NS_ERROR_NET_TIMEOUT ||
|
|
aStatus == NS_ERROR_REDIRECT_LOOP ||
|
|
aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
|
|
aStatus == NS_ERROR_NET_INTERRUPT ||
|
|
aStatus == NS_ERROR_NET_RESET ||
|
|
aStatus == NS_ERROR_MALWARE_URI ||
|
|
aStatus == NS_ERROR_PHISHING_URI ||
|
|
aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
|
|
NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
|
|
DisplayLoadError(aStatus, url, nsnull, channel);
|
|
}
|
|
else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
|
|
/* A document that was requested to be fetched *only* from
|
|
* the cache is not in cache. May be this is one of those
|
|
* postdata results. Throw a dialog to the user,
|
|
* saying that the page has expired from cache and ask if
|
|
* they wish to refetch the page from the net. Do this only
|
|
* if the request is a form post.
|
|
*/
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
|
nsCAutoString method;
|
|
if (httpChannel)
|
|
httpChannel->GetRequestMethod(method);
|
|
if (method.Equals("POST") && !NS_IsOffline()) {
|
|
PRBool repost;
|
|
rv = ConfirmRepost(&repost);
|
|
if (NS_FAILED(rv)) return rv;
|
|
// If the user pressed cancel in the dialog, return. Don't try to load
|
|
// the page without the post data.
|
|
if (!repost)
|
|
return NS_OK;
|
|
|
|
// The user wants to repost the data to the server.
|
|
// If the page was loaded due to a back/forward/go
|
|
// operation, update the session history index.
|
|
// This is similar to the updating done in
|
|
// nsDocShell::OnNewURI() for regular pages
|
|
nsCOMPtr<nsISHistory> rootSH=mSessionHistory;
|
|
if (!mSessionHistory) {
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
//Get the root docshell
|
|
GetSameTypeRootTreeItem(getter_AddRefs(root));
|
|
if (root) {
|
|
// QI root to nsIWebNavigation
|
|
nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
|
|
if (rootAsWebnav) {
|
|
// Get the handle to SH from the root docshell
|
|
rootAsWebnav->GetSessionHistory(getter_AddRefs(rootSH));
|
|
}
|
|
}
|
|
} // mSessionHistory
|
|
|
|
if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
|
|
nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
|
|
if (shInternal) {
|
|
rootSH->GetIndex(&mPreviousTransIndex);
|
|
shInternal->UpdateIndex();
|
|
rootSH->GetIndex(&mLoadedTransIndex);
|
|
#ifdef DEBUG_PAGE_CACHE
|
|
printf("Previous index: %d, Loaded index: %d\n\n",
|
|
mPreviousTransIndex, mLoadedTransIndex);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Make it look like we really did honestly finish loading the
|
|
// history page we were loading, since the "reload" load we're
|
|
// about to kick off will reload our current history entry. This
|
|
// is a bit of a hack, and if the force-load fails I think we'll
|
|
// end up being confused about what page we're on... but we would
|
|
// anyway, since we've updated the session history index above.
|
|
SetHistoryEntry(&mOSHE, loadingSHE);
|
|
|
|
// The user does want to repost the data to the server.
|
|
// Initiate a new load again.
|
|
|
|
// Get the postdata if any from the channel.
|
|
nsCOMPtr<nsIInputStream> inputStream;
|
|
nsCOMPtr<nsIURI> referrer;
|
|
if (channel) {
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
|
|
|
if(httpChannel) {
|
|
httpChannel->GetReferrer(getter_AddRefs(referrer));
|
|
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(channel));
|
|
if (uploadChannel) {
|
|
uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
|
|
}
|
|
}
|
|
}
|
|
nsCOMPtr<nsISeekableStream> postDataSeekable(do_QueryInterface(inputStream));
|
|
if (postDataSeekable)
|
|
{
|
|
postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
|
}
|
|
InternalLoad(url, // URI
|
|
referrer, // Referring URI
|
|
nsnull, // Owner
|
|
INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner
|
|
nsnull, // No window target
|
|
nsnull, // No type hint
|
|
inputStream, // Post data stream
|
|
nsnull, // No headers stream
|
|
LOAD_RELOAD_BYPASS_PROXY_AND_CACHE,// Load type
|
|
nsnull, // No SHEntry
|
|
PR_TRUE, // first party site
|
|
nsnull, // No nsIDocShell
|
|
nsnull); // No nsIRequest
|
|
}
|
|
else {
|
|
DisplayLoadError(aStatus, url, nsnull, channel);
|
|
}
|
|
}
|
|
} // if we have a host
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// Routines for selection and clipboard
|
|
//
|
|
|
|
#ifdef XP_MAC
|
|
#pragma mark -
|
|
#endif
|
|
|
|
nsresult
|
|
nsWebShell::GetControllerForCommand ( const char * inCommand, nsIController** outController )
|
|
{
|
|
NS_ENSURE_ARG_POINTER(outController);
|
|
*outController = nsnull;
|
|
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window ( do_QueryInterface(mScriptGlobal) );
|
|
if ( window ) {
|
|
nsIFocusController *focusController = window->GetRootFocusController();
|
|
if ( focusController )
|
|
rv = focusController->GetControllerForCommand ( inCommand, outController );
|
|
} // if window
|
|
|
|
return rv;
|
|
|
|
} // GetControllerForCommand
|
|
|
|
|
|
nsresult
|
|
nsWebShell::IsCommandEnabled ( const char * inCommand, PRBool* outEnabled )
|
|
{
|
|
NS_ENSURE_ARG_POINTER(outEnabled);
|
|
*outEnabled = PR_FALSE;
|
|
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIController> controller;
|
|
rv = GetControllerForCommand ( inCommand, getter_AddRefs(controller) );
|
|
if ( controller )
|
|
rv = controller->IsCommandEnabled(inCommand, outEnabled);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsWebShell::DoCommand ( const char * inCommand )
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIController> controller;
|
|
rv = GetControllerForCommand ( inCommand, getter_AddRefs(controller) );
|
|
if ( controller )
|
|
rv = controller->DoCommand(inCommand);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CanCutSelection(PRBool* aResult)
|
|
{
|
|
return IsCommandEnabled ( "cmd_cut", aResult );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CanCopySelection(PRBool* aResult)
|
|
{
|
|
return IsCommandEnabled ( "cmd_copy", aResult );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CanCopyLinkLocation(PRBool* aResult)
|
|
{
|
|
return IsCommandEnabled ( "cmd_copyLink", aResult );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CanCopyImageLocation(PRBool* aResult)
|
|
{
|
|
return IsCommandEnabled ( "cmd_copyImageLocation",
|
|
aResult );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CanCopyImageContents(PRBool* aResult)
|
|
{
|
|
return IsCommandEnabled ( "cmd_copyImageContents",
|
|
aResult );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CanPaste(PRBool* aResult)
|
|
{
|
|
return IsCommandEnabled ( "cmd_paste", aResult );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CutSelection(void)
|
|
{
|
|
return DoCommand ( "cmd_cut" );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CopySelection(void)
|
|
{
|
|
return DoCommand ( "cmd_copy" );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CopyLinkLocation(void)
|
|
{
|
|
return DoCommand ( "cmd_copyLink" );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CopyImageLocation(void)
|
|
{
|
|
return DoCommand ( "cmd_copyImageLocation" );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::CopyImageContents(void)
|
|
{
|
|
return DoCommand ( "cmd_copyImageContents" );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::Paste(void)
|
|
{
|
|
return DoCommand ( "cmd_paste" );
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebShell::SelectAll(void)
|
|
{
|
|
return DoCommand ( "cmd_selectAll" );
|
|
}
|
|
|
|
|
|
//
|
|
// SelectNone
|
|
//
|
|
// Collapses the current selection, insertion point ends up at beginning
|
|
// of previous selection.
|
|
//
|
|
NS_IMETHODIMP
|
|
nsWebShell::SelectNone(void)
|
|
{
|
|
return DoCommand ( "cmd_selectNone" );
|
|
}
|
|
|
|
|
|
#ifdef XP_MAC
|
|
#pragma mark -
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
// nsWebShell::nsIBaseWindow
|
|
//*****************************************************************************
|
|
|
|
NS_IMETHODIMP nsWebShell::Create()
|
|
{
|
|
if (mPrefs) {
|
|
// We've already been created
|
|
return NS_OK;
|
|
}
|
|
|
|
WEB_TRACE(WEB_TRACE_CALLS,
|
|
("nsWebShell::Init: this=%p", this));
|
|
|
|
return nsDocShell::Create();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
unsigned long nsWebShell::gNumberOfWebShells = 0;
|
|
#endif
|