파이어폭스에서 Content-Disposition의 동작이 크롬이나 인터넷익스플러어랑 다른 것을 확인했다.
어떻게 구현되었는지 궁금해서 소스코드를 체크아웃 해서 확인해보았다.
1. Tortoise Hg 설치(hg 클라이언트 명령 도구를 위함)
2. 코드 체크아웃
> hg clone https://hg.mozilla.org/mozilla-central/ firefox
Content-Disposition 구현 코드
시작은 ExternalHelperAppParent 클래스의 Init메서드부터이다.
mContentDispositionHeader.IsEmpty() 메서드에 의해 Content-Disposition 헤더가 비어있지 않으면 NS_GetFilenameFromDisposition 헬퍼함수에 의해 mContentDispositionFilename 맴버변수로 파일이름을 획득하게 된다.
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());
}
helperAppService->DoContent(aMimeContentType, this, window,
aForceSave, nullptr,
getter_AddRefs(mListener));
}
위치: firefox/netwerk/base/nsNetUtil.h (network가 아닌 netwerk인줄 모르겠다. 일부러 철자를 틀리게 쓴 건가?)
함수명: NS_GetFilenameFromDisposition
헤더에 구현이 되어 있는 이유는 인라인함수이기 때문이었다.
함수 코드는 아래와 같다. (저장소 주소: 링크)
do_GetService 함수는 파이어폭스 곳곳에서 볼 수 있는데 이 함수를 알려면 먼저 XPCOM에대해 알아야 한다.
XPCOM(cross platform component object model)은 마이크로소프트의 COM과 유사한 크로스 플랫폼 컴포넌트 객체 모델이다. 자세한 것은 https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM를 참고하자.
inline nsresult
NS_GetFilenameFromDisposition(nsAString& aFilename,
const nsACString& aDisposition,
nsIURI* aURI = nullptr)
{
aFilename.Truncate();
nsresult rv;
nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar =
do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
nsAutoCString fallbackCharset;
if (url)
url->GetOriginCharset(fallbackCharset);
// Get the value of 'filename' parameter
rv = mimehdrpar->GetParameterHTTP(aDisposition, "filename",
fallbackCharset, true, nullptr,
aFilename);
if (NS_FAILED(rv)) {
aFilename.Truncate();
return rv;
}
if (aFilename.IsEmpty())
return NS_ERROR_NOT_AVAILABLE;
return NS_OK;
}
실제 do_GetService 인라인함수는 firefox/xpcom/glue/nsServiceManagerUtils.h에 정의되어 있는 함수로 다음과 같이 오버로딩되어 있다.
inline const nsGetServiceByCID do_GetService(const nsCID& aCID)
{
return nsGetServiceByCID(aCID);
}
inline const nsGetServiceByCIDWithError do_GetService(const nsCID& aCID, nsresult* aError)
{
return nsGetServiceByCIDWithError(aCID, aError);
}
inline const nsGetServiceByContractID do_GetService(const char* aContractID)
{
return nsGetServiceByContractID(aContractID);
}
inline const
nsGetServiceByContractIDWithError do_GetService(const char* aContractID,
nsresult* aError)
{
return nsGetServiceByContractIDWithError(aContractID, aError);
}
MIME에 대해 filename이라는 파라메터를 구해오도록 호출하는데, nsMIMEHeaderParamImpl 클래스의 메서드는 래퍼 함수인 DoGetParameter를 호출하도록 되어있다.
위치: firefox/netwerk/mime/nsMIMEHeaderParamImpl.cpp
NS_IMETHODIMP
nsMIMEHeaderParamImpl::GetParameterHTTP(const nsACString& aHeaderVal,
const char *aParamName,
const nsACString& aFallbackCharset,
bool aTryLocaleCharset,
char **aLang, nsAString& aResult)
{
return DoGetParameter(aHeaderVal, aParamName, HTTP_FIELD_ENCODING,
aFallbackCharset, aTryLocaleCharset, aLang, aResult);
}
nsresult
nsMIMEHeaderParamImpl::DoGetParameter(const nsACString& aHeaderVal,
const char *aParamName,
ParamDecoding aDecoding,
const nsACString& aFallbackCharset,
bool aTryLocaleCharset,
char **aLang, nsAString& aResult)
{
aResult.Truncate();
nsresult rv;
// get parameter (decode RFC 2231/5987 when applicable, as specified by
// aDecoding (5987 being a subset of 2231) and return charset.)
nsXPIDLCString med;
nsXPIDLCString charset;
rv = DoParameterInternal(PromiseFlatCString(aHeaderVal).get(), aParamName,
aDecoding, getter_Copies(charset), aLang,
getter_Copies(med));
if (NS_FAILED(rv))
return rv;
// convert to UTF-8 after charset conversion and RFC 2047 decoding
// if necessary.
nsAutoCString str1;
rv = internalDecodeParameter(med, charset.get(), nullptr, false,
// was aDecoding == MIME_FIELD_ENCODING
// see bug 875615
true,
str1);
NS_ENSURE_SUCCESS(rv, rv);
if (!aFallbackCharset.IsEmpty())
{
nsAutoCString charset;
EncodingUtils::FindEncodingForLabel(aFallbackCharset, charset);
nsAutoCString str2;
nsCOMPtr<nsIUTF8ConverterService>
cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID));
if (cvtUTF8 &&
NS_SUCCEEDED(cvtUTF8->ConvertStringToUTF8(str1,
PromiseFlatCString(aFallbackCharset).get(), false,
!charset.EqualsLiteral("UTF-8"),
1, str2))) {
CopyUTF8toUTF16(str2, aResult);
return NS_OK;
}
}
if (IsUTF8(str1)) {
CopyUTF8toUTF16(str1, aResult);
return NS_OK;
}
if (aTryLocaleCharset && !NS_IsNativeUTF8())
return NS_CopyNativeToUnicode(str1, aResult);
CopyASCIItoUTF16(str1, aResult);
return NS_OK;
}
해당 MIME 을 테스트하는 코드는 아래와 같다.
위치: firefox/netwerk/test/unit/test_MIME_params.js (링크)
'Applications' 카테고리의 다른 글
[chrome] DHC - REST/HTTP API Client (0) | 2015.07.01 |
---|---|
Google Code Project Hosting의 지원중단 예고 (0) | 2015.03.18 |
[크롬 플러그인] 유튜브 광고 뛰어넘기/다운로드 익스텐션 (0) | 2015.02.21 |
redmine - c# 문법 하이라이트 (0) | 2014.07.17 |
[엑셀] 랜덤 낱말 만들기 (0) | 2014.03.07 |