1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/html/document/src/MediaDocument.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,412 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "MediaDocument.h" 1.10 +#include "nsGkAtoms.h" 1.11 +#include "nsRect.h" 1.12 +#include "nsPresContext.h" 1.13 +#include "nsIPresShell.h" 1.14 +#include "nsIScrollable.h" 1.15 +#include "nsViewManager.h" 1.16 +#include "nsITextToSubURI.h" 1.17 +#include "nsIURL.h" 1.18 +#include "nsIContentViewer.h" 1.19 +#include "nsIMarkupDocumentViewer.h" 1.20 +#include "nsIDocShell.h" 1.21 +#include "nsCharsetSource.h" // kCharsetFrom* macro definition 1.22 +#include "nsNodeInfoManager.h" 1.23 +#include "nsContentUtils.h" 1.24 +#include "nsDocElementCreatedNotificationRunner.h" 1.25 +#include "mozilla/Services.h" 1.26 +#include "nsServiceManagerUtils.h" 1.27 +#include "nsIPrincipal.h" 1.28 + 1.29 +namespace mozilla { 1.30 +namespace dom { 1.31 + 1.32 +MediaDocumentStreamListener::MediaDocumentStreamListener(MediaDocument *aDocument) 1.33 +{ 1.34 + mDocument = aDocument; 1.35 +} 1.36 + 1.37 +MediaDocumentStreamListener::~MediaDocumentStreamListener() 1.38 +{ 1.39 +} 1.40 + 1.41 + 1.42 +NS_IMPL_ISUPPORTS(MediaDocumentStreamListener, 1.43 + nsIRequestObserver, 1.44 + nsIStreamListener) 1.45 + 1.46 + 1.47 +void 1.48 +MediaDocumentStreamListener::SetStreamListener(nsIStreamListener *aListener) 1.49 +{ 1.50 + mNextStream = aListener; 1.51 +} 1.52 + 1.53 +NS_IMETHODIMP 1.54 +MediaDocumentStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt) 1.55 +{ 1.56 + NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE); 1.57 + 1.58 + mDocument->StartLayout(); 1.59 + 1.60 + if (mNextStream) { 1.61 + return mNextStream->OnStartRequest(request, ctxt); 1.62 + } 1.63 + 1.64 + return NS_BINDING_ABORTED; 1.65 +} 1.66 + 1.67 +NS_IMETHODIMP 1.68 +MediaDocumentStreamListener::OnStopRequest(nsIRequest* request, 1.69 + nsISupports *ctxt, 1.70 + nsresult status) 1.71 +{ 1.72 + nsresult rv = NS_OK; 1.73 + if (mNextStream) { 1.74 + rv = mNextStream->OnStopRequest(request, ctxt, status); 1.75 + } 1.76 + 1.77 + // No more need for our document so clear our reference and prevent leaks 1.78 + mDocument = nullptr; 1.79 + 1.80 + return rv; 1.81 +} 1.82 + 1.83 +NS_IMETHODIMP 1.84 +MediaDocumentStreamListener::OnDataAvailable(nsIRequest* request, 1.85 + nsISupports *ctxt, 1.86 + nsIInputStream *inStr, 1.87 + uint64_t sourceOffset, 1.88 + uint32_t count) 1.89 +{ 1.90 + if (mNextStream) { 1.91 + return mNextStream->OnDataAvailable(request, ctxt, inStr, sourceOffset, count); 1.92 + } 1.93 + 1.94 + return NS_OK; 1.95 +} 1.96 + 1.97 +// default format names for MediaDocument. 1.98 +const char* const MediaDocument::sFormatNames[4] = 1.99 +{ 1.100 + "MediaTitleWithNoInfo", // eWithNoInfo 1.101 + "MediaTitleWithFile", // eWithFile 1.102 + "", // eWithDim 1.103 + "" // eWithDimAndFile 1.104 +}; 1.105 + 1.106 +MediaDocument::MediaDocument() 1.107 + : nsHTMLDocument(), 1.108 + mDocumentElementInserted(false) 1.109 +{ 1.110 +} 1.111 +MediaDocument::~MediaDocument() 1.112 +{ 1.113 +} 1.114 + 1.115 +nsresult 1.116 +MediaDocument::Init() 1.117 +{ 1.118 + nsresult rv = nsHTMLDocument::Init(); 1.119 + NS_ENSURE_SUCCESS(rv, rv); 1.120 + 1.121 + // Create a bundle for the localization 1.122 + nsCOMPtr<nsIStringBundleService> stringService = 1.123 + mozilla::services::GetStringBundleService(); 1.124 + if (stringService) { 1.125 + stringService->CreateBundle(NSMEDIADOCUMENT_PROPERTIES_URI, 1.126 + getter_AddRefs(mStringBundle)); 1.127 + } 1.128 + 1.129 + mIsSyntheticDocument = true; 1.130 + 1.131 + return NS_OK; 1.132 +} 1.133 + 1.134 +nsresult 1.135 +MediaDocument::StartDocumentLoad(const char* aCommand, 1.136 + nsIChannel* aChannel, 1.137 + nsILoadGroup* aLoadGroup, 1.138 + nsISupports* aContainer, 1.139 + nsIStreamListener** aDocListener, 1.140 + bool aReset, 1.141 + nsIContentSink* aSink) 1.142 +{ 1.143 + nsresult rv = nsDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, 1.144 + aContainer, aDocListener, aReset, 1.145 + aSink); 1.146 + if (NS_FAILED(rv)) { 1.147 + return rv; 1.148 + } 1.149 + 1.150 + // We try to set the charset of the current document to that of the 1.151 + // 'genuine' (as opposed to an intervening 'chrome') parent document 1.152 + // that may be in a different window/tab. Even if we fail here, 1.153 + // we just return NS_OK because another attempt is made in 1.154 + // |UpdateTitleAndCharset| and the worst thing possible is a mangled 1.155 + // filename in the titlebar and the file picker. 1.156 + 1.157 + // Note that we 1.158 + // exclude UTF-8 as 'invalid' because UTF-8 is likely to be the charset 1.159 + // of a chrome document that has nothing to do with the actual content 1.160 + // whose charset we want to know. Even if "the actual content" is indeed 1.161 + // in UTF-8, we don't lose anything because the default empty value is 1.162 + // considered synonymous with UTF-8. 1.163 + 1.164 + nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer)); 1.165 + 1.166 + // not being able to set the charset is not critical. 1.167 + NS_ENSURE_TRUE(docShell, NS_OK); 1.168 + 1.169 + nsAutoCString charset; 1.170 + int32_t source; 1.171 + nsCOMPtr<nsIPrincipal> principal; 1.172 + // opening in a new tab 1.173 + docShell->GetParentCharset(charset, &source, getter_AddRefs(principal)); 1.174 + 1.175 + if (!charset.IsEmpty() && 1.176 + !charset.Equals("UTF-8") && 1.177 + NodePrincipal()->Equals(principal)) { 1.178 + SetDocumentCharacterSetSource(source); 1.179 + SetDocumentCharacterSet(charset); 1.180 + } 1.181 + 1.182 + return NS_OK; 1.183 +} 1.184 + 1.185 +void 1.186 +MediaDocument::BecomeInteractive() 1.187 +{ 1.188 + // In principle, if we knew the readyState code to work, we could infer 1.189 + // restoration from GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE. 1.190 + bool restoring = false; 1.191 + nsPIDOMWindow* window = GetWindow(); 1.192 + if (window) { 1.193 + nsIDocShell* docShell = window->GetDocShell(); 1.194 + if (docShell) { 1.195 + docShell->GetRestoringDocument(&restoring); 1.196 + } 1.197 + } 1.198 + if (!restoring) { 1.199 + MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING, 1.200 + "Bad readyState"); 1.201 + SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE); 1.202 + } 1.203 +} 1.204 + 1.205 +nsresult 1.206 +MediaDocument::CreateSyntheticDocument() 1.207 +{ 1.208 + // Synthesize an empty html document 1.209 + nsresult rv; 1.210 + 1.211 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.212 + nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nullptr, 1.213 + kNameSpaceID_XHTML, 1.214 + nsIDOMNode::ELEMENT_NODE); 1.215 + 1.216 + nsRefPtr<nsGenericHTMLElement> root = NS_NewHTMLHtmlElement(nodeInfo.forget()); 1.217 + NS_ENSURE_TRUE(root, NS_ERROR_OUT_OF_MEMORY); 1.218 + 1.219 + NS_ASSERTION(GetChildCount() == 0, "Shouldn't have any kids"); 1.220 + rv = AppendChildTo(root, false); 1.221 + NS_ENSURE_SUCCESS(rv, rv); 1.222 + 1.223 + nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::head, nullptr, 1.224 + kNameSpaceID_XHTML, 1.225 + nsIDOMNode::ELEMENT_NODE); 1.226 + 1.227 + // Create a <head> so our title has somewhere to live 1.228 + nsRefPtr<nsGenericHTMLElement> head = NS_NewHTMLHeadElement(nodeInfo.forget()); 1.229 + NS_ENSURE_TRUE(head, NS_ERROR_OUT_OF_MEMORY); 1.230 + 1.231 + nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::meta, nullptr, 1.232 + kNameSpaceID_XHTML, 1.233 + nsIDOMNode::ELEMENT_NODE); 1.234 + 1.235 + nsRefPtr<nsGenericHTMLElement> metaContent = NS_NewHTMLMetaElement(nodeInfo.forget()); 1.236 + NS_ENSURE_TRUE(metaContent, NS_ERROR_OUT_OF_MEMORY); 1.237 + metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::name, 1.238 + NS_LITERAL_STRING("viewport"), 1.239 + true); 1.240 + 1.241 + metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::content, 1.242 + NS_LITERAL_STRING("width=device-width; height=device-height;"), 1.243 + true); 1.244 + head->AppendChildTo(metaContent, false); 1.245 + 1.246 + root->AppendChildTo(head, false); 1.247 + 1.248 + nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::body, nullptr, 1.249 + kNameSpaceID_XHTML, 1.250 + nsIDOMNode::ELEMENT_NODE); 1.251 + 1.252 + nsRefPtr<nsGenericHTMLElement> body = NS_NewHTMLBodyElement(nodeInfo.forget()); 1.253 + NS_ENSURE_TRUE(body, NS_ERROR_OUT_OF_MEMORY); 1.254 + 1.255 + root->AppendChildTo(body, false); 1.256 + 1.257 + return NS_OK; 1.258 +} 1.259 + 1.260 +nsresult 1.261 +MediaDocument::StartLayout() 1.262 +{ 1.263 + mMayStartLayout = true; 1.264 + nsCOMPtr<nsIPresShell> shell = GetShell(); 1.265 + // Don't mess with the presshell if someone has already handled 1.266 + // its initial reflow. 1.267 + if (shell && !shell->DidInitialize()) { 1.268 + nsRect visibleArea = shell->GetPresContext()->GetVisibleArea(); 1.269 + nsresult rv = shell->Initialize(visibleArea.width, visibleArea.height); 1.270 + NS_ENSURE_SUCCESS(rv, rv); 1.271 + } 1.272 + 1.273 + return NS_OK; 1.274 +} 1.275 + 1.276 +void 1.277 +MediaDocument::GetFileName(nsAString& aResult) 1.278 +{ 1.279 + aResult.Truncate(); 1.280 + 1.281 + nsCOMPtr<nsIURL> url = do_QueryInterface(mDocumentURI); 1.282 + if (!url) 1.283 + return; 1.284 + 1.285 + nsAutoCString fileName; 1.286 + url->GetFileName(fileName); 1.287 + if (fileName.IsEmpty()) 1.288 + return; 1.289 + 1.290 + nsAutoCString docCharset; 1.291 + // Now that the charset is set in |StartDocumentLoad| to the charset of 1.292 + // the document viewer instead of a bogus value ("ISO-8859-1" set in 1.293 + // |nsDocument|'s ctor), the priority is given to the current charset. 1.294 + // This is necessary to deal with a media document being opened in a new 1.295 + // window or a new tab, in which case |originCharset| of |nsIURI| is not 1.296 + // reliable. 1.297 + if (mCharacterSetSource != kCharsetUninitialized) { 1.298 + docCharset = mCharacterSet; 1.299 + } else { 1.300 + // resort to |originCharset| 1.301 + url->GetOriginCharset(docCharset); 1.302 + SetDocumentCharacterSet(docCharset); 1.303 + } 1.304 + 1.305 + nsresult rv; 1.306 + nsCOMPtr<nsITextToSubURI> textToSubURI = 1.307 + do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); 1.308 + if (NS_SUCCEEDED(rv)) { 1.309 + // UnEscapeURIForUI always succeeds 1.310 + textToSubURI->UnEscapeURIForUI(docCharset, fileName, aResult); 1.311 + } else { 1.312 + CopyUTF8toUTF16(fileName, aResult); 1.313 + } 1.314 +} 1.315 + 1.316 +nsresult 1.317 +MediaDocument::LinkStylesheet(const nsAString& aStylesheet) 1.318 +{ 1.319 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.320 + nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::link, nullptr, 1.321 + kNameSpaceID_XHTML, 1.322 + nsIDOMNode::ELEMENT_NODE); 1.323 + 1.324 + nsRefPtr<nsGenericHTMLElement> link = NS_NewHTMLLinkElement(nodeInfo.forget()); 1.325 + NS_ENSURE_TRUE(link, NS_ERROR_OUT_OF_MEMORY); 1.326 + 1.327 + link->SetAttr(kNameSpaceID_None, nsGkAtoms::rel, 1.328 + NS_LITERAL_STRING("stylesheet"), true); 1.329 + 1.330 + link->SetAttr(kNameSpaceID_None, nsGkAtoms::href, aStylesheet, true); 1.331 + 1.332 + Element* head = GetHeadElement(); 1.333 + return head->AppendChildTo(link, false); 1.334 +} 1.335 + 1.336 +void 1.337 +MediaDocument::UpdateTitleAndCharset(const nsACString& aTypeStr, 1.338 + const char* const* aFormatNames, 1.339 + int32_t aWidth, int32_t aHeight, 1.340 + const nsAString& aStatus) 1.341 +{ 1.342 + nsXPIDLString fileStr; 1.343 + GetFileName(fileStr); 1.344 + 1.345 + NS_ConvertASCIItoUTF16 typeStr(aTypeStr); 1.346 + nsXPIDLString title; 1.347 + 1.348 + if (mStringBundle) { 1.349 + // if we got a valid size (not all media have a size) 1.350 + if (aWidth != 0 && aHeight != 0) { 1.351 + nsAutoString widthStr; 1.352 + nsAutoString heightStr; 1.353 + widthStr.AppendInt(aWidth); 1.354 + heightStr.AppendInt(aHeight); 1.355 + // If we got a filename, display it 1.356 + if (!fileStr.IsEmpty()) { 1.357 + const char16_t *formatStrings[4] = {fileStr.get(), typeStr.get(), 1.358 + widthStr.get(), heightStr.get()}; 1.359 + NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithDimAndFile]); 1.360 + mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 4, 1.361 + getter_Copies(title)); 1.362 + } 1.363 + else { 1.364 + const char16_t *formatStrings[3] = {typeStr.get(), widthStr.get(), 1.365 + heightStr.get()}; 1.366 + NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithDim]); 1.367 + mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 3, 1.368 + getter_Copies(title)); 1.369 + } 1.370 + } 1.371 + else { 1.372 + // If we got a filename, display it 1.373 + if (!fileStr.IsEmpty()) { 1.374 + const char16_t *formatStrings[2] = {fileStr.get(), typeStr.get()}; 1.375 + NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithFile]); 1.376 + mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2, 1.377 + getter_Copies(title)); 1.378 + } 1.379 + else { 1.380 + const char16_t *formatStrings[1] = {typeStr.get()}; 1.381 + NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithNoInfo]); 1.382 + mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 1, 1.383 + getter_Copies(title)); 1.384 + } 1.385 + } 1.386 + } 1.387 + 1.388 + // set it on the document 1.389 + if (aStatus.IsEmpty()) { 1.390 + SetTitle(title); 1.391 + } 1.392 + else { 1.393 + nsXPIDLString titleWithStatus; 1.394 + const nsPromiseFlatString& status = PromiseFlatString(aStatus); 1.395 + const char16_t *formatStrings[2] = {title.get(), status.get()}; 1.396 + NS_NAMED_LITERAL_STRING(fmtName, "TitleWithStatus"); 1.397 + mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2, 1.398 + getter_Copies(titleWithStatus)); 1.399 + SetTitle(titleWithStatus); 1.400 + } 1.401 +} 1.402 + 1.403 +void 1.404 +MediaDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject) 1.405 +{ 1.406 + nsHTMLDocument::SetScriptGlobalObject(aGlobalObject); 1.407 + if (!mDocumentElementInserted && aGlobalObject) { 1.408 + mDocumentElementInserted = true; 1.409 + nsContentUtils::AddScriptRunner( 1.410 + new nsDocElementCreatedNotificationRunner(this)); 1.411 + } 1.412 +} 1.413 + 1.414 +} // namespace dom 1.415 +} // namespace mozilla