fixes bug 97528 "1200 urls created on startup about:blank" (take 2)

r=dp, sr=brendan
This commit is contained in:
darin%netscape.com 2001-09-14 21:19:41 +00:00
Родитель 051c211df1
Коммит 819e81b079
12 изменённых файлов: 231 добавлений и 945 удалений

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

@ -270,16 +270,20 @@ NS_IMETHODIMP
nsJARChannel::AsyncOpen(nsIStreamListener* listener, nsISupports* ctxt)
{
nsresult rv;
mUserContext = ctxt;
mUserListener = listener;
mSynchronousRead = PR_FALSE;
if (mLoadGroup) {
rv = mLoadGroup->AddRequest(this, nsnull);
if (NS_FAILED(rv)) return rv;
}
mSynchronousRead = PR_FALSE;
return EnsureJARFileAvailable();
rv = EnsureJARFileAvailable();
if (NS_FAILED(rv) && mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, rv);
return rv;
}
nsresult
@ -288,30 +292,43 @@ nsJARChannel::EnsureJARFileAvailable()
nsresult rv;
#ifdef PR_LOGGING
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: EnsureJARFileAvailable %s", (const char*)jarURLStr));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: EnsureJARFileAvailable %s", (const char*)jarURLStr));
}
#endif
rv = mURI->GetJARFile(getter_AddRefs(mJARBaseURI));
if (NS_FAILED(rv)) goto error;
if (NS_FAILED(rv)) return rv;
rv = mURI->GetJAREntry(&mJAREntry);
if (NS_FAILED(rv)) goto error;
if (NS_FAILED(rv)) return rv;
rv = NS_NewDownloader(getter_AddRefs(mDownloader),
mJARBaseURI, this, nsnull, mSynchronousRead,
mLoadGroup, mCallbacks, mLoadFlags);
// try to get a nsIFile directly from the url, which will often succeed.
{
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJARBaseURI);
if (fileURL)
fileURL->GetFile(getter_AddRefs(mDownloadedJARFile));
}
// if DownloadComplete() was called early, need to release the reference.
if (mSynchronousRead && mSynchronousInputStream)
mDownloader = null_nsCOMPtr();
if (mDownloadedJARFile) {
// after successfully downloading the jar file to the cache,
// start the extraction process:
if (mSynchronousRead)
rv = OpenJARElement();
else
rv = AsyncReadJARElement();
}
else {
rv = NS_NewDownloader(getter_AddRefs(mDownloader),
mJARBaseURI, this, nsnull, mSynchronousRead,
mLoadGroup, mCallbacks, mLoadFlags);
error:
if (NS_FAILED(rv) && mLoadGroup) {
nsresult rv2 = mLoadGroup->RemoveRequest(this, nsnull, NS_OK);
NS_ASSERTION(NS_SUCCEEDED(rv2), "RemoveChannel failed");
// if DownloadComplete() was called early, need to release the reference.
if (mSynchronousRead && mSynchronousInputStream)
mDownloader = 0;
}
return rv;
}
@ -340,10 +357,12 @@ nsJARChannel::AsyncReadJARElement()
}
#ifdef PR_LOGGING
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: AsyncRead jar entry %s", (const char*)jarURLStr));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: AsyncRead jar entry %s", (const char*)jarURLStr));
}
#endif
rv = jarTransport->AsyncRead(this, nsnull, 0, PRUint32(-1), 0,
@ -566,13 +585,15 @@ nsJARChannel::OnStopRequest(nsIRequest* jarExtractionTransport, nsISupports* con
{
nsresult rv;
#ifdef PR_LOGGING
nsCOMPtr<nsIURI> jarURI;
nsXPIDLCString jarURLStr;
rv = mURI->GetSpec(getter_Copies(jarURLStr));
if (NS_SUCCEEDED(rv)) {
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: jar extraction complete %s status=%x",
(const char*)jarURLStr, aStatus));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsCOMPtr<nsIURI> jarURI;
nsXPIDLCString jarURLStr;
rv = mURI->GetSpec(getter_Copies(jarURLStr));
if (NS_SUCCEEDED(rv)) {
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: jar extraction complete %s status=%x",
(const char*)jarURLStr, aStatus));
}
}
#endif
@ -656,10 +677,12 @@ NS_IMETHODIMP
nsJARChannel::GetInputStream(nsIInputStream* *aInputStream)
{
#ifdef PR_LOGGING
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: GetInputStream jar entry %s", (const char*)jarURLStr));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: GetInputStream jar entry %s", (const char*)jarURLStr));
}
#endif
NS_ENSURE_TRUE(mJAR, NS_ERROR_NULL_POINTER);
return mJAR->GetInputStream(mJAREntry, aInputStream);

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

@ -270,16 +270,20 @@ NS_IMETHODIMP
nsJARChannel::AsyncOpen(nsIStreamListener* listener, nsISupports* ctxt)
{
nsresult rv;
mUserContext = ctxt;
mUserListener = listener;
mSynchronousRead = PR_FALSE;
if (mLoadGroup) {
rv = mLoadGroup->AddRequest(this, nsnull);
if (NS_FAILED(rv)) return rv;
}
mSynchronousRead = PR_FALSE;
return EnsureJARFileAvailable();
rv = EnsureJARFileAvailable();
if (NS_FAILED(rv) && mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, rv);
return rv;
}
nsresult
@ -288,30 +292,43 @@ nsJARChannel::EnsureJARFileAvailable()
nsresult rv;
#ifdef PR_LOGGING
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: EnsureJARFileAvailable %s", (const char*)jarURLStr));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: EnsureJARFileAvailable %s", (const char*)jarURLStr));
}
#endif
rv = mURI->GetJARFile(getter_AddRefs(mJARBaseURI));
if (NS_FAILED(rv)) goto error;
if (NS_FAILED(rv)) return rv;
rv = mURI->GetJAREntry(&mJAREntry);
if (NS_FAILED(rv)) goto error;
if (NS_FAILED(rv)) return rv;
rv = NS_NewDownloader(getter_AddRefs(mDownloader),
mJARBaseURI, this, nsnull, mSynchronousRead,
mLoadGroup, mCallbacks, mLoadFlags);
// try to get a nsIFile directly from the url, which will often succeed.
{
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJARBaseURI);
if (fileURL)
fileURL->GetFile(getter_AddRefs(mDownloadedJARFile));
}
// if DownloadComplete() was called early, need to release the reference.
if (mSynchronousRead && mSynchronousInputStream)
mDownloader = null_nsCOMPtr();
if (mDownloadedJARFile) {
// after successfully downloading the jar file to the cache,
// start the extraction process:
if (mSynchronousRead)
rv = OpenJARElement();
else
rv = AsyncReadJARElement();
}
else {
rv = NS_NewDownloader(getter_AddRefs(mDownloader),
mJARBaseURI, this, nsnull, mSynchronousRead,
mLoadGroup, mCallbacks, mLoadFlags);
error:
if (NS_FAILED(rv) && mLoadGroup) {
nsresult rv2 = mLoadGroup->RemoveRequest(this, nsnull, NS_OK);
NS_ASSERTION(NS_SUCCEEDED(rv2), "RemoveChannel failed");
// if DownloadComplete() was called early, need to release the reference.
if (mSynchronousRead && mSynchronousInputStream)
mDownloader = 0;
}
return rv;
}
@ -340,10 +357,12 @@ nsJARChannel::AsyncReadJARElement()
}
#ifdef PR_LOGGING
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: AsyncRead jar entry %s", (const char*)jarURLStr));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: AsyncRead jar entry %s", (const char*)jarURLStr));
}
#endif
rv = jarTransport->AsyncRead(this, nsnull, 0, PRUint32(-1), 0,
@ -566,13 +585,15 @@ nsJARChannel::OnStopRequest(nsIRequest* jarExtractionTransport, nsISupports* con
{
nsresult rv;
#ifdef PR_LOGGING
nsCOMPtr<nsIURI> jarURI;
nsXPIDLCString jarURLStr;
rv = mURI->GetSpec(getter_Copies(jarURLStr));
if (NS_SUCCEEDED(rv)) {
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: jar extraction complete %s status=%x",
(const char*)jarURLStr, aStatus));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsCOMPtr<nsIURI> jarURI;
nsXPIDLCString jarURLStr;
rv = mURI->GetSpec(getter_Copies(jarURLStr));
if (NS_SUCCEEDED(rv)) {
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: jar extraction complete %s status=%x",
(const char*)jarURLStr, aStatus));
}
}
#endif
@ -656,10 +677,12 @@ NS_IMETHODIMP
nsJARChannel::GetInputStream(nsIInputStream* *aInputStream)
{
#ifdef PR_LOGGING
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: GetInputStream jar entry %s", (const char*)jarURLStr));
if (PR_LOG_TEST(gJarProtocolLog, PR_LOG_DEBUG)) {
nsXPIDLCString jarURLStr;
mURI->GetSpec(getter_Copies(jarURLStr));
PR_LOG(gJarProtocolLog, PR_LOG_DEBUG,
("nsJarProtocol: GetInputStream jar entry %s", (const char*)jarURLStr));
}
#endif
NS_ENSURE_TRUE(mJAR, NS_ERROR_NULL_POINTER);
return mJAR->GetInputStream(mJAREntry, aInputStream);

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

@ -75,4 +75,9 @@ interface nsIResProtocolHandler : nsIProtocolHandler
* Returns true if substitutions are already defined for the specified root.
*/
boolean hasSubstitutions(in string root);
/**
* Resolution utility function.
*/
string resolveURI(in nsIURI resourceURI);
};

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

@ -31,7 +31,6 @@ LIBRARY_NAME = nkres_s
REQUIRES = xpcom string exthandler mimetype
CPPSRCS = \
nsResChannel.cpp \
nsResProtocolHandler.cpp \
$(NULL)
@ -41,6 +40,10 @@ EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS)
# static lib.
FORCE_STATIC_LIB = 1
LOCAL_INCLUDES = \
-I$(DEPTH)/netwerk/base/src \
$(NULL)
include $(topsrcdir)/config/rules.mk
ifeq ($(OS_ARCH), Linux)

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

@ -28,10 +28,13 @@ LIBRARY_NAME=nkres_s
LCFLAGS = -DWIN32_LEAN_AND_MEAN -D_IMPL_NS_NET
CPP_OBJS= \
.\$(OBJDIR)\nsResChannel.obj \
.\$(OBJDIR)\nsResProtocolHandler.obj \
$(NULL)
INCS = $(INCS) \
-I$(DEPTH)\netwerk\base\src \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)

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

@ -1,651 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsResChannel.h"
#include "nsCRT.h"
#include "netCore.h"
#include "nsIServiceManager.h"
#include "nsIFileTransportService.h"
#include "nsIComponentManager.h"
#include "nsIURL.h"
#include "nsIIOService.h"
#include "nsXPIDLString.h"
#include "nsCExternalHandlerService.h"
#include "nsIMIMEService.h"
#include "nsNetUtil.h"
#include "nsMimeTypes.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
#if defined(PR_LOGGING)
//
// Log module for nsResChannel logging...
//
// To enable logging (see prlog.h for full details):
//
// set NSPR_LOG_MODULES=nsResChannel:5
// set NSPR_LOG_FILE=nspr.log
//
// this enables PR_LOG_DEBUG level information and places all output in
// the file nspr.log
PRLogModuleInfo* gResChannelLog = nsnull;
#endif /* PR_LOGGING */
////////////////////////////////////////////////////////////////////////////////
nsResChannel::nsResChannel()
: mLoadFlags(nsIResChannel::LOAD_NORMAL),
mState(QUIESCENT),
mStatus(NS_OK)
#ifdef DEBUG
,mInitiator(nsnull)
#endif
{
NS_INIT_REFCNT();
#if defined(PR_LOGGING)
//
// Initialize the global PRLogModule for socket transport logging
// if necessary...
//
if (nsnull == gResChannelLog) {
gResChannelLog = PR_NewLogModule("nsResChannel");
}
#endif /* PR_LOGGING */
}
nsresult
nsResChannel::Init(nsIResProtocolHandler* handler, nsIURI* uri)
{
mHandler = handler;
mResourceURI = uri;
return NS_OK;
}
nsResChannel::~nsResChannel()
{
}
NS_IMPL_THREADSAFE_ADDREF(nsResChannel)
NS_IMPL_THREADSAFE_RELEASE(nsResChannel)
NS_INTERFACE_MAP_BEGIN(nsResChannel)
NS_INTERFACE_MAP_ENTRY(nsIResChannel)
NS_INTERFACE_MAP_ENTRY(nsIFileChannel)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIResChannel)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIRequest, nsIResChannel)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIChannel, nsIResChannel)
NS_INTERFACE_MAP_END_THREADSAFE
NS_METHOD
nsResChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
{
nsResChannel* rc = new nsResChannel();
if (rc == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(rc);
nsresult rv = rc->QueryInterface(aIID, aResult);
NS_RELEASE(rc);
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// nsResChannel::Substitutions
////////////////////////////////////////////////////////////////////////////////
nsresult
nsResChannel::Substitutions::Init()
{
nsresult rv;
nsResChannel* channel = GET_SUBSTITUTIONS_CHANNEL(this);
if (mSubstitutions)
return NS_ERROR_FAILURE;
char* root;
rv = channel->mResourceURI->GetHost(&root);
if (NS_SUCCEEDED(rv)) {
// XXX don't pass null to GetSubstitutions!
const char* strRoot = root ? root : "";
rv = channel->mHandler->GetSubstitutions(strRoot,
getter_AddRefs(mSubstitutions));
nsCRT::free(root);
}
return rv;
}
nsresult
nsResChannel::Substitutions::Next(char* *result)
{
nsresult rv;
nsResChannel* channel = GET_SUBSTITUTIONS_CHANNEL(this);
nsCOMPtr<nsIURI> substURI;
rv = mSubstitutions->GetElementAt(mCurrentIndex++, getter_AddRefs(substURI));
if (NS_FAILED(rv)) return rv;
if (substURI == nsnull)
return NS_ERROR_FAILURE;
char* path = nsnull;
rv = channel->mResourceURI->GetPath(&path);
if (NS_FAILED(rv)) return rv;
// XXX this path[0] check is a hack -- it seems to me that GetPath
// shouldn't include the leading slash:
char* aResolvedURI;
rv = substURI->Resolve(path[0] == '/' ? path+1 : path, &aResolvedURI);
nsCRT::free(path);
if (NS_FAILED(rv)) return rv;
*result = aResolvedURI;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIRequest methods:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsResChannel::GetName(PRUnichar* *result)
{
nsresult rv;
nsXPIDLCString urlStr;
rv = mResourceURI->GetSpec(getter_Copies(urlStr));
if (NS_FAILED(rv)) return rv;
nsString name;
name.AppendWithConversion(urlStr);
*result = name.ToNewUnicode();
return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsResChannel::IsPending(PRBool *result)
{
if (mResolvedChannel)
return mResolvedChannel->IsPending(result);
*result = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::GetStatus(nsresult *status)
{
*status = mStatus;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::Cancel(nsresult status)
{
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
#ifdef DEBUG
NS_ASSERTION(mInitiator == PR_CurrentThread(),
"wrong thread calling this routine");
#endif
nsresult rv = NS_OK;
if (mResolvedChannel) {
rv = mResolvedChannel->Cancel(status);
}
mStatus = status;
mResolvedChannel = nsnull; // remove the resolution
return rv;
}
NS_IMETHODIMP
nsResChannel::Suspend()
{
#ifdef DEBUG
NS_ASSERTION(mInitiator == PR_CurrentThread(),
"wrong thread calling this routine");
#endif
if (mResolvedChannel)
return mResolvedChannel->Suspend();
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::Resume()
{
#ifdef DEBUG
NS_ASSERTION(mInitiator == PR_CurrentThread(),
"wrong thread calling this routine");
#endif
if (mResolvedChannel)
return mResolvedChannel->Resume();
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIChannel methods:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsResChannel::GetOriginalURI(nsIURI* *aURI)
{
*aURI = mOriginalURI ? mOriginalURI : mResourceURI;
NS_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::SetOriginalURI(nsIURI* aURI)
{
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::GetURI(nsIURI* *aURI)
{
if (mResolvedChannel) {
return mResolvedChannel->GetURI(aURI);
}
*aURI = mResourceURI;
NS_ADDREF(*aURI);
return NS_OK;
}
nsresult
nsResChannel::EnsureNextResolvedChannel()
{
nsresult rv;
nsXPIDLCString resolvedURI;
nsCOMPtr<nsIIOService> serv(do_GetService(kIOServiceCID, &rv));
if (NS_FAILED(rv)) goto done;
rv = mSubstitutions.Next(getter_Copies(resolvedURI));
if (NS_FAILED(rv)) goto done;
rv = serv->NewChannel(resolvedURI, nsnull,
getter_AddRefs(mResolvedChannel));
if (NS_FAILED(rv)) {
rv = NS_OK; // returning NS_OK lets us try again
goto done;
}
if (mOriginalURI) {
rv = mResolvedChannel->SetOriginalURI(mOriginalURI);
if (NS_FAILED(rv)) goto done;
}
if (mLoadGroup) {
rv = mResolvedChannel->SetLoadGroup(mLoadGroup);
if (NS_FAILED(rv)) goto done;
}
if (mLoadFlags != nsIResChannel::LOAD_NORMAL) {
rv = mResolvedChannel->SetLoadFlags(mLoadFlags);
if (NS_FAILED(rv)) goto done;
}
if (mCallbacks) {
rv = mResolvedChannel->SetNotificationCallbacks(mCallbacks);
if (NS_FAILED(rv)) goto done;
}
done:
#if defined(PR_LOGGING)
nsXPIDLCString resURI;
(void)mResourceURI->GetSpec(getter_Copies(resURI));
PR_LOG(gResChannelLog, PR_LOG_DEBUG,
("nsResChannel: resolving %s ",
(const char*)resURI));
PR_LOG(gResChannelLog, PR_LOG_DEBUG,
(" to %s => status %x\n",
(const char*)resolvedURI, rv));
#endif /* PR_LOGGING */
return rv;
}
NS_IMETHODIMP
nsResChannel::Open(nsIInputStream **result)
{
nsresult rv;
if (mResolvedChannel)
return NS_ERROR_IN_PROGRESS;
rv = mSubstitutions.Init();
if (NS_FAILED(rv)) return rv;
do {
rv = EnsureNextResolvedChannel();
if (NS_FAILED(rv)) break;
if (mResolvedChannel)
rv = mResolvedChannel->Open(result);
} while (NS_FAILED(rv));
return rv;
}
// XXX What does OpenOutputStream mean for a res "channel"
#if 0
NS_IMETHODIMP
nsResChannel::OpenOutputStream(PRUint32 transferOffset, PRUint32 transferCount, nsIOutputStream **result)
{
nsresult rv;
if (mResolvedChannel)
return NS_ERROR_IN_PROGRESS;
rv = mSubstitutions.Init();
if (NS_FAILED(rv)) return rv;
do {
rv = EnsureNextResolvedChannel();
if (NS_FAILED(rv)) break;
if (mResolvedChannel)
rv = mResolvedChannel->OpenOutputStream(transferOffset, transferCount, result);
} while (NS_FAILED(rv));
return rv;
}
#endif
NS_IMETHODIMP
nsResChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
{
nsresult rv;
#ifdef DEBUG
NS_ASSERTION(mInitiator == nsnull || mInitiator == PR_CurrentThread(),
"wrong thread calling this routine");
mInitiator = PR_CurrentThread();
#endif
switch (mState) {
case QUIESCENT:
if (mResolvedChannel)
return NS_ERROR_IN_PROGRESS;
// first time through
rv = mSubstitutions.Init();
if (NS_FAILED(rv)) return rv;
// fall through
mState = ASYNC_READ;
case ASYNC_READ:
break;
default:
return NS_ERROR_IN_PROGRESS;
}
NS_ASSERTION(mState == ASYNC_READ, "wrong state");
mUserContext = ctxt;
mUserObserver = listener; // cast back to nsIStreamListener later
do {
rv = EnsureNextResolvedChannel();
if (NS_FAILED(rv)) break;
if (mResolvedChannel)
rv = mResolvedChannel->AsyncOpen(this, nsnull);
// Later, this AsyncRead will call back our OnStopRequest
// method. The action resumes there...
} while (NS_FAILED(rv));
if (NS_FAILED(rv))
(void)EndRequest(rv);
return rv;
}
NS_IMETHODIMP
nsResChannel::GetLoadFlags(PRUint32 *aLoadFlags)
{
*aLoadFlags = mLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::SetLoadFlags(PRUint32 aLoadFlags)
{
mLoadFlags = aLoadFlags;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::GetContentType(char * *aContentType)
{
if (mResolvedChannel)
return mResolvedChannel->GetContentType(aContentType);
// if we have not created a mResolvedChannel, use the mime service
nsCOMPtr<nsIMIMEService> MIMEService (do_GetService(NS_MIMESERVICE_CONTRACTID));
if (!MIMEService) return NS_ERROR_FAILURE;
return MIMEService->GetTypeFromURI(mResourceURI, aContentType);
}
NS_IMETHODIMP
nsResChannel::SetContentType(const char *aContentType)
{
if (mResolvedChannel)
return mResolvedChannel->SetContentType(aContentType);
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsResChannel::GetContentLength(PRInt32 *aContentLength)
{
if (mResolvedChannel)
return mResolvedChannel->GetContentLength(aContentLength);
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsResChannel::SetContentLength(PRInt32 aContentLength)
{
NS_NOTREACHED("nsResChannel::SetContentLength");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsResChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup;
NS_IF_ADDREF(*aLoadGroup);
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
*aNotificationCallbacks = mCallbacks.get();
NS_IF_ADDREF(*aNotificationCallbacks);
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
mCallbacks = aNotificationCallbacks;
return NS_OK;
}
NS_IMETHODIMP
nsResChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
NS_NOTREACHED("nsResChannel::GetSecurityInfo");
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////////////
// nsIStreamListener methods:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsResChannel::OnStartRequest(nsIRequest* request, nsISupports* context)
{
#ifdef DEBUG
NS_ASSERTION(mInitiator == PR_CurrentThread(),
"wrong thread calling this routine");
#endif
NS_ASSERTION(mUserObserver, "No observer...");
return mUserObserver->OnStartRequest(NS_STATIC_CAST(nsIResChannel*, this), mUserContext);
}
NS_IMETHODIMP
nsResChannel::OnStopRequest(nsIRequest* request, nsISupports* context,
nsresult aStatus)
{
#ifdef DEBUG
NS_ASSERTION(mInitiator == PR_CurrentThread(),
"wrong thread calling this routine");
#endif
if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED) {
nsCOMPtr<nsIRequest> dummyRequest;
// if we failed to process this channel, then try the next one:
switch (mState) {
case ASYNC_READ:
return AsyncOpen(GetUserListener(), mUserContext);
default:
break;
}
}
return EndRequest(aStatus);
}
nsresult
nsResChannel::EndRequest(nsresult aStatus)
{
nsresult rv;
rv = mUserObserver->OnStopRequest(NS_STATIC_CAST(nsIResChannel*, this),
mUserContext, aStatus);
#if 0 // we don't add the resource channel to the group (although maybe we should)
if (mLoadGroup) {
if (NS_SUCCEEDED(rv)) {
mLoadGroup->RemoveChannel(this, context, notif);
}
}
#endif
// Release the reference to the consumer stream listener...
mUserObserver = 0;
mUserContext = 0;
mResolvedChannel = 0;
return rv;
}
NS_IMETHODIMP
nsResChannel::OnDataAvailable(nsIRequest* request, nsISupports* context,
nsIInputStream *aIStream, PRUint32 aSourceOffset,
PRUint32 aLength)
{
#ifdef DEBUG
NS_ASSERTION(mInitiator == PR_CurrentThread(),
"wrong thread calling this routine");
#endif
return GetUserListener()->OnDataAvailable(NS_STATIC_CAST(nsIResChannel*, this),
mUserContext, aIStream,
aSourceOffset, aLength);
}
////////////////////////////////////////////////////////////////////////////////
/* void init (in nsIFile file, in long ioFlags, in long perm); */
NS_IMETHODIMP nsResChannel::Init(nsIFile *file, PRInt32 ioFlags, PRInt32 perm)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute nsIFile file; */
NS_IMETHODIMP nsResChannel::GetFile(nsIFile * *result)
{
nsresult rv;
rv = mSubstitutions.Init();
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIFile> file;
do {
rv = EnsureNextResolvedChannel();
if (NS_FAILED(rv)) break;
if (mResolvedChannel) {
nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mResolvedChannel));
if (fc) {
rv = fc->GetFile(getter_AddRefs(file));
if (file) {
PRBool exists;
rv = file->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists) {
*result = file;
NS_ADDREF(*result);
return NS_OK;
}
}
}
}
} while (NS_FAILED(rv));
*result = nsnull;
return NS_ERROR_FAILURE;
}
/* attribute long ioFlags; */
NS_IMETHODIMP nsResChannel::GetIoFlags(PRInt32 *aIoFlags)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsResChannel::SetIoFlags(PRInt32 aIoFlags)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* attribute long permissions; */
NS_IMETHODIMP nsResChannel::GetPermissions(PRInt32 *aPermissions)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsResChannel::SetPermissions(PRInt32 aPermissions)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

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

@ -1,130 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef nsResChannel_h__
#define nsResChannel_h__
#include "nsIResChannel.h"
#include "nsIFileChannel.h"
#include "nsIStreamListener.h"
#include "nsIStreamProvider.h"
#include "nsIResProtocolHandler.h"
#include "nsIURI.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsILoadGroup.h"
#include "nsIInputStream.h"
#include "nsISupportsArray.h"
#include "nsCOMPtr.h"
#include "nsAutoLock.h"
#ifdef DEBUG
#include "prthread.h"
#endif
class nsResChannel : public nsIResChannel,
public nsIFileChannel,
public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
NS_DECL_NSICHANNEL
NS_DECL_NSIFILECHANNEL
NS_DECL_NSIRESCHANNEL
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
nsResChannel();
virtual ~nsResChannel();
// Define a Create method to be used with a factory:
static NS_METHOD
Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult);
nsresult Init(nsIResProtocolHandler* handler, nsIURI* uri);
protected:
class Substitutions {
public:
Substitutions() : mCurrentIndex(0) {}
~Substitutions() {}
nsresult Init();
nsresult Next(char* *result);
protected:
nsCOMPtr<nsIURI> mResourceURI;
nsCOMPtr<nsISupportsArray> mSubstitutions;
PRUint32 mCurrentIndex;
};
friend class Substitutions;
#define GET_SUBSTITUTIONS_CHANNEL(_this) \
((nsResChannel*)((char*)(_this) - offsetof(nsResChannel, mSubstitutions)))
enum State {
QUIESCENT,
ASYNC_READ,
ASYNC_WRITE
};
nsIStreamListener* GetUserListener() {
// this method doesn't addref the listener
NS_ASSERTION(mState == ASYNC_READ, "wrong state");
// this cast is safe because we set mUserObserver in AsyncRead
nsIRequestObserver* obs = mUserObserver;
nsIStreamListener* listener = NS_STATIC_CAST(nsIStreamListener*, obs);
return listener;
}
nsIStreamProvider* GetUserProvider() {
// this method doesn't addref the provider
NS_ASSERTION(mState == ASYNC_WRITE, "wrong state");
// this cast is safe because we set mUserObserver in AsyncWrite
nsIRequestObserver* obs = mUserObserver;
nsIStreamProvider* provider = NS_STATIC_CAST(nsIStreamProvider*, obs);
return provider;
}
nsresult EnsureNextResolvedChannel();
nsresult EndRequest(nsresult aStatus);
protected:
nsCOMPtr<nsIURI> mOriginalURI;
nsCOMPtr<nsIURI> mResourceURI;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
PRUint32 mLoadFlags;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsISupports> mOwner;
nsCOMPtr<nsIResProtocolHandler> mHandler;
nsCOMPtr<nsIChannel> mResolvedChannel;
State mState;
Substitutions mSubstitutions;
nsCOMPtr<nsIRequestObserver> mUserObserver;
nsCOMPtr<nsISupports> mUserContext;
nsresult mStatus;
#ifdef DEBUG
PRThread* mInitiator;
#endif
};
#endif // nsResChannel_h__

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

@ -19,6 +19,7 @@
*
* Contributor(s):
* IBM Corp.
* Darin Fisher <darin@netscape.com>
*/
#include "nsResProtocolHandler.h"
@ -26,25 +27,65 @@
#include "nsIURL.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsResChannel.h"
#include "nsIFileChannel.h"
#include "nsILocalFile.h"
#include "prenv.h"
#include "prmem.h"
#include "prprf.h"
#include "nsXPIDLString.h"
#include "nsIFile.h"
#include "nsDirectoryServiceDefs.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
////////////////////////////////////////////////////////////////////////////////
// nsResURL : overrides nsStdURL::GetFile to provide nsIFile resolution
#include "nsStdURL.h"
class nsResURL : public nsStdURL
{
public:
NS_IMETHOD GetFile(nsIFile **);
};
NS_IMETHODIMP
nsResURL::GetFile(nsIFile **result)
{
nsresult rv;
NS_ENSURE_TRUE(nsResProtocolHandler::get(), NS_ERROR_NOT_AVAILABLE);
nsXPIDLCString spec;
rv = nsResProtocolHandler::get()->ResolveURI(this, getter_Copies(spec));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILocalFile> localFile =
do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = localFile->SetURL(spec);
if (NS_FAILED(rv)) return rv;
return CallQueryInterface(localFile, result);
}
////////////////////////////////////////////////////////////////////////////////
nsResProtocolHandler *nsResProtocolHandler::mGlobalInstance = nsnull;
nsResProtocolHandler::nsResProtocolHandler()
: mLock(nsnull), mSubstitutions(32)
: mSubstitutions(32)
{
NS_INIT_REFCNT();
NS_ASSERTION(!mGlobalInstance, "res handler already created!");
mGlobalInstance = this;
}
nsResProtocolHandler::~nsResProtocolHandler()
{
mGlobalInstance = nsnull;
}
nsresult
@ -55,14 +96,8 @@ nsResProtocolHandler::SetSpecialDir(const char* rootName, const char* sysDir)
rv = NS_GetSpecialDirectory(sysDir, getter_AddRefs(file));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIFileURL> fileURL(do_CreateInstance(kStandardURLCID, &rv));
if (NS_FAILED(rv)) return rv;
rv = fileURL->SetFile(file);
if (NS_FAILED(rv)) return rv;
nsXPIDLCString spec;
rv = fileURL->GetSpec(getter_Copies(spec));
rv = file->GetURL(getter_Copies(spec));
if (NS_FAILED(rv)) return rv;
return AppendSubstitution(rootName, spec);
@ -73,9 +108,8 @@ nsResProtocolHandler::Init()
{
nsresult rv;
mLock = PR_NewLock();
if (mLock == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
mIOService = do_GetIOService(&rv);
if (NS_FAILED(rv)) return rv;
// set up initial mappings
rv = SetSpecialDir("ProgramDir", NS_OS_CURRENT_PROCESS_DIR);
@ -121,12 +155,6 @@ nsResProtocolHandler::Init()
return rv;
}
nsResProtocolHandler::~nsResProtocolHandler()
{
if (mLock)
PR_DestroyLock(mLock);
}
NS_IMPL_THREADSAFE_ISUPPORTS3(nsResProtocolHandler, nsIResProtocolHandler, nsIProtocolHandler, nsISupportsWeakReference)
NS_METHOD
@ -179,23 +207,16 @@ nsResProtocolHandler::NewURI(const char *aSpec, nsIURI *aBaseURI,
{
nsresult rv;
nsCOMPtr<nsIURI> url(do_CreateInstance(kStandardURLCID, &rv));
if (NS_FAILED(rv)) return rv;
nsResURL *resURL;
NS_NEWXPCOM(resURL, nsResURL);
if (!resURL)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(resURL);
if (aBaseURI) {
nsXPIDLCString aResolvedURI;
rv = aBaseURI->Resolve(aSpec, getter_Copies(aResolvedURI));
if (NS_FAILED(rv)) return rv;
rv = url->SetSpec(aResolvedURI);
} else {
rv = url->SetSpec((char*)aSpec);
}
if (NS_FAILED(rv))
return rv;
*result = url;
NS_ADDREF(*result);
rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec, aBaseURI);
if (NS_SUCCEEDED(rv))
rv = CallQueryInterface(resURL, result);
NS_RELEASE(resURL);
return rv;
}
@ -203,19 +224,15 @@ NS_IMETHODIMP
nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
{
nsresult rv;
nsResChannel* channel;
rv = nsResChannel::Create(nsnull, NS_GET_IID(nsIResChannel), (void**)&channel);
nsXPIDLCString spec;
rv = ResolveURI(uri, getter_Copies(spec));
if (NS_FAILED(rv)) return rv;
rv = channel->Init(this, uri);
if (NS_FAILED(rv)) {
NS_RELEASE(channel);
return rv;
}
rv = mIOService->NewChannel(spec, nsnull, result);
if (NS_FAILED(rv)) return rv;
*result = (nsIChannel*)(nsIResChannel*)channel;
return NS_OK;
return (*result)->SetOriginalURI(uri);
}
NS_IMETHODIMP
@ -231,12 +248,9 @@ NS_IMETHODIMP
nsResProtocolHandler::PrependSubstitution(const char *root, const char *urlStr)
{
nsresult rv;
nsAutoLock lock(mLock);
nsCOMPtr<nsIIOService> ioServ = do_GetIOService(&rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIURI> url;
rv = ioServ->NewURI(urlStr, nsnull, getter_AddRefs(url));
rv = mIOService->NewURI(urlStr, nsnull, getter_AddRefs(url));
if (NS_FAILED(rv)) return rv;
nsCStringKey key(root);
@ -268,12 +282,9 @@ NS_IMETHODIMP
nsResProtocolHandler::AppendSubstitution(const char *root, const char *urlStr)
{
nsresult rv;
nsAutoLock lock(mLock);
nsCOMPtr<nsIIOService> ioServ = do_GetIOService(&rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIURI> url;
rv = ioServ->NewURI(urlStr, nsnull, getter_AddRefs(url));
rv = mIOService->NewURI(urlStr, nsnull, getter_AddRefs(url));
if (NS_FAILED(rv)) return rv;
nsCStringKey key(root);
@ -324,12 +335,9 @@ NS_IMETHODIMP
nsResProtocolHandler::RemoveSubstitution(const char *root, const char *urlStr)
{
nsresult rv;
nsAutoLock lock(mLock);
nsCOMPtr<nsIIOService> ioServ = do_GetIOService(&rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIURI> url;
rv = ioServ->NewURI(urlStr, nsnull, getter_AddRefs(url));
rv = mIOService->NewURI(urlStr, nsnull, getter_AddRefs(url));
if (NS_FAILED(rv)) return rv;
nsCStringKey key(root);
@ -361,7 +369,6 @@ NS_IMETHODIMP
nsResProtocolHandler::GetSubstitutions(const char *root, nsISupportsArray* *result)
{
nsresult rv;
nsAutoLock lock(mLock);
nsCStringKey key(root);
nsISupportsArray* strings = (nsISupportsArray*)mSubstitutions.Get(&key);
@ -382,4 +389,35 @@ nsResProtocolHandler::HasSubstitutions(const char *root, PRBool *result)
return NS_OK;
}
NS_IMETHODIMP
nsResProtocolHandler::ResolveURI(nsIURI *uri, char **result)
{
nsresult rv;
nsXPIDLCString host, path;
rv = uri->GetHost(getter_Copies(host));
if (NS_FAILED(rv)) return rv;
rv = uri->GetPath(getter_Copies(path));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISupportsArray> substitutions;
rv = GetSubstitutions(host.get() ?
host.get() : "", getter_AddRefs(substitutions));
if (NS_FAILED(rv)) return rv;
// always use the first substitution
nsCOMPtr<nsIURI> substURI;
substitutions->GetElementAt(0, getter_AddRefs(substURI));
if (!substURI) return NS_ERROR_NOT_AVAILABLE;
rv = substURI->Resolve(path[0] == '/' ? path+1 : path, result);
#if 0
nsXPIDLCString spec;
uri->GetSpec(getter_Copies(spec));
printf("%s\n -> %s\n", spec.get(), *result);
#endif
return rv;
}
////////////////////////////////////////////////////////////////////////////////

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

@ -18,6 +18,7 @@
* Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@netscape.com>
*/
#ifndef nsResProtocolHandler_h___
@ -26,6 +27,7 @@
#include "nsIResProtocolHandler.h"
#include "nsHashtable.h"
#include "nsISupportsArray.h"
#include "nsIIOService.h"
#include "nsWeakReference.h"
class nsResProtocolHandler : public nsIResProtocolHandler, public nsSupportsWeakReference
@ -45,9 +47,13 @@ public:
nsresult Init();
nsresult SetSpecialDir(const char* rootName, const char* specialDir);
protected:
PRLock* mLock;
nsSupportsHashtable mSubstitutions;
static nsResProtocolHandler *get() { return mGlobalInstance; }
private:
static nsResProtocolHandler *mGlobalInstance;
nsSupportsHashtable mSubstitutions;
nsCOMPtr<nsIIOService> mIOService;
};
#endif /* nsResProtocolHandler_h___ */

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

@ -1,34 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsIGenericFactory.h"
#include "nsResProtocolHandler.h"
static nsModuleComponentInfo gResComponents[] = {
{ "The Resource Protocol Handler",
NS_RESPROTOCOLHANDLER_CID,
NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "resource",
nsResProtocolHandler::Create
}
2};
NS_IMPL_NSGETMODULE(res, gResComponents)

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

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