forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			440 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			440 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "MediaDocument.h"
 | 
						|
#include "nsGkAtoms.h"
 | 
						|
#include "nsRect.h"
 | 
						|
#include "nsPresContext.h"
 | 
						|
#include "nsIPresShell.h"
 | 
						|
#include "nsIScrollable.h"
 | 
						|
#include "nsViewManager.h"
 | 
						|
#include "nsITextToSubURI.h"
 | 
						|
#include "nsIURL.h"
 | 
						|
#include "nsIContentViewer.h"
 | 
						|
#include "nsIDocShell.h"
 | 
						|
#include "nsCharsetSource.h" // kCharsetFrom* macro definition
 | 
						|
#include "nsNodeInfoManager.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsDocElementCreatedNotificationRunner.h"
 | 
						|
#include "mozilla/Services.h"
 | 
						|
#include "nsServiceManagerUtils.h"
 | 
						|
#include "nsIPrincipal.h"
 | 
						|
#include "nsIMultiPartChannel.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace dom {
 | 
						|
 | 
						|
MediaDocumentStreamListener::MediaDocumentStreamListener(MediaDocument *aDocument)
 | 
						|
{
 | 
						|
  mDocument = aDocument;
 | 
						|
}
 | 
						|
 | 
						|
MediaDocumentStreamListener::~MediaDocumentStreamListener()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(MediaDocumentStreamListener,
 | 
						|
                  nsIRequestObserver,
 | 
						|
                  nsIStreamListener)
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
MediaDocumentStreamListener::SetStreamListener(nsIStreamListener *aListener)
 | 
						|
{
 | 
						|
  mNextStream = aListener;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
MediaDocumentStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
 | 
						|
{
 | 
						|
  NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  mDocument->StartLayout();
 | 
						|
 | 
						|
  if (mNextStream) {
 | 
						|
    return mNextStream->OnStartRequest(request, ctxt);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_ERROR_PARSED_DATA_CACHED;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
MediaDocumentStreamListener::OnStopRequest(nsIRequest* request,
 | 
						|
                                           nsISupports *ctxt,
 | 
						|
                                           nsresult status)
 | 
						|
{
 | 
						|
  nsresult rv = NS_OK;
 | 
						|
  if (mNextStream) {
 | 
						|
    rv = mNextStream->OnStopRequest(request, ctxt, status);
 | 
						|
  }
 | 
						|
 | 
						|
  // Don't release mDocument here if we're in the middle of a multipart response.
 | 
						|
  bool lastPart = true;
 | 
						|
  nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(request));
 | 
						|
  if (mpchan) {
 | 
						|
    mpchan->GetIsLastPart(&lastPart);
 | 
						|
  }
 | 
						|
 | 
						|
  if (lastPart) {
 | 
						|
    mDocument = nullptr;
 | 
						|
  }
 | 
						|
  return rv;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
MediaDocumentStreamListener::OnDataAvailable(nsIRequest* request,
 | 
						|
                                             nsISupports *ctxt,
 | 
						|
                                             nsIInputStream *inStr,
 | 
						|
                                             uint64_t sourceOffset,
 | 
						|
                                             uint32_t count)
 | 
						|
{
 | 
						|
  if (mNextStream) {
 | 
						|
    return mNextStream->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
// default format names for MediaDocument. 
 | 
						|
const char* const MediaDocument::sFormatNames[4] = 
 | 
						|
{
 | 
						|
  "MediaTitleWithNoInfo",    // eWithNoInfo
 | 
						|
  "MediaTitleWithFile",      // eWithFile
 | 
						|
  "",                        // eWithDim
 | 
						|
  ""                         // eWithDimAndFile
 | 
						|
};
 | 
						|
 | 
						|
MediaDocument::MediaDocument()
 | 
						|
    : nsHTMLDocument(),
 | 
						|
      mDocumentElementInserted(false)
 | 
						|
{
 | 
						|
}
 | 
						|
MediaDocument::~MediaDocument()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
MediaDocument::Init()
 | 
						|
{
 | 
						|
  nsresult rv = nsHTMLDocument::Init();
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  // Create a bundle for the localization
 | 
						|
  nsCOMPtr<nsIStringBundleService> stringService =
 | 
						|
    mozilla::services::GetStringBundleService();
 | 
						|
  if (stringService) {
 | 
						|
    stringService->CreateBundle(NSMEDIADOCUMENT_PROPERTIES_URI,
 | 
						|
                                getter_AddRefs(mStringBundle));
 | 
						|
  }
 | 
						|
 | 
						|
  mIsSyntheticDocument = true;
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
MediaDocument::StartDocumentLoad(const char*         aCommand,
 | 
						|
                                 nsIChannel*         aChannel,
 | 
						|
                                 nsILoadGroup*       aLoadGroup,
 | 
						|
                                 nsISupports*        aContainer,
 | 
						|
                                 nsIStreamListener** aDocListener,
 | 
						|
                                 bool                aReset,
 | 
						|
                                 nsIContentSink*     aSink)
 | 
						|
{
 | 
						|
  nsresult rv = nsDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup,
 | 
						|
                                              aContainer, aDocListener, aReset,
 | 
						|
                                              aSink);
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  // We try to set the charset of the current document to that of the 
 | 
						|
  // 'genuine' (as opposed to an intervening 'chrome') parent document 
 | 
						|
  // that may be in a different window/tab. Even if we fail here,
 | 
						|
  // we just return NS_OK because another attempt is made in 
 | 
						|
  // |UpdateTitleAndCharset| and the worst thing possible is a mangled 
 | 
						|
  // filename in the titlebar and the file picker.
 | 
						|
 | 
						|
  // Note that we
 | 
						|
  // exclude UTF-8 as 'invalid' because UTF-8 is likely to be the charset 
 | 
						|
  // of a chrome document that has nothing to do with the actual content 
 | 
						|
  // whose charset we want to know. Even if "the actual content" is indeed 
 | 
						|
  // in UTF-8, we don't lose anything because the default empty value is 
 | 
						|
  // considered synonymous with UTF-8. 
 | 
						|
    
 | 
						|
  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
 | 
						|
 | 
						|
  // not being able to set the charset is not critical.
 | 
						|
  NS_ENSURE_TRUE(docShell, NS_OK); 
 | 
						|
 | 
						|
  nsAutoCString charset;
 | 
						|
  int32_t source;
 | 
						|
  nsCOMPtr<nsIPrincipal> principal;
 | 
						|
  // opening in a new tab
 | 
						|
  docShell->GetParentCharset(charset, &source, getter_AddRefs(principal));
 | 
						|
 | 
						|
  if (!charset.IsEmpty() &&
 | 
						|
      !charset.EqualsLiteral("UTF-8") &&
 | 
						|
      NodePrincipal()->Equals(principal)) {
 | 
						|
    SetDocumentCharacterSetSource(source);
 | 
						|
    SetDocumentCharacterSet(charset);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
MediaDocument::BecomeInteractive()
 | 
						|
{
 | 
						|
  // Even though our readyState code isn't really reliable, here we pretend
 | 
						|
  // that it is and conclude that we are restoring from the b/f cache if
 | 
						|
  // GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE.
 | 
						|
  if (GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
 | 
						|
    MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING,
 | 
						|
               "Bad readyState");
 | 
						|
    SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
MediaDocument::CreateSyntheticDocument()
 | 
						|
{
 | 
						|
  // Synthesize an empty html document
 | 
						|
  nsresult rv;
 | 
						|
 | 
						|
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
 | 
						|
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nullptr,
 | 
						|
                                           kNameSpaceID_XHTML,
 | 
						|
                                           nsIDOMNode::ELEMENT_NODE);
 | 
						|
 | 
						|
  RefPtr<nsGenericHTMLElement> root = NS_NewHTMLHtmlElement(nodeInfo.forget());
 | 
						|
  NS_ENSURE_TRUE(root, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
 | 
						|
  NS_ASSERTION(GetChildCount() == 0, "Shouldn't have any kids");
 | 
						|
  rv = AppendChildTo(root, false);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::head, nullptr,
 | 
						|
                                           kNameSpaceID_XHTML,
 | 
						|
                                           nsIDOMNode::ELEMENT_NODE);
 | 
						|
 | 
						|
  // Create a <head> so our title has somewhere to live
 | 
						|
  RefPtr<nsGenericHTMLElement> head = NS_NewHTMLHeadElement(nodeInfo.forget());
 | 
						|
  NS_ENSURE_TRUE(head, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
 | 
						|
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::meta, nullptr,
 | 
						|
                                           kNameSpaceID_XHTML,
 | 
						|
                                           nsIDOMNode::ELEMENT_NODE);
 | 
						|
 | 
						|
  RefPtr<nsGenericHTMLElement> metaContent = NS_NewHTMLMetaElement(nodeInfo.forget());
 | 
						|
  NS_ENSURE_TRUE(metaContent, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
  metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
 | 
						|
                       NS_LITERAL_STRING("viewport"),
 | 
						|
                       true);
 | 
						|
 | 
						|
  metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
 | 
						|
                       NS_LITERAL_STRING("width=device-width; height=device-height;"),
 | 
						|
                       true);
 | 
						|
  head->AppendChildTo(metaContent, false);
 | 
						|
 | 
						|
  root->AppendChildTo(head, false);
 | 
						|
 | 
						|
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::body, nullptr,
 | 
						|
                                           kNameSpaceID_XHTML,
 | 
						|
                                           nsIDOMNode::ELEMENT_NODE);
 | 
						|
 | 
						|
  RefPtr<nsGenericHTMLElement> body = NS_NewHTMLBodyElement(nodeInfo.forget());
 | 
						|
  NS_ENSURE_TRUE(body, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
 | 
						|
  root->AppendChildTo(body, false);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
MediaDocument::StartLayout()
 | 
						|
{
 | 
						|
  mMayStartLayout = true;
 | 
						|
  nsCOMPtr<nsIPresShell> shell = GetShell();
 | 
						|
  // Don't mess with the presshell if someone has already handled
 | 
						|
  // its initial reflow.
 | 
						|
  if (shell && !shell->DidInitialize()) {
 | 
						|
    nsRect visibleArea = shell->GetPresContext()->GetVisibleArea();
 | 
						|
    nsresult rv = shell->Initialize(visibleArea.width, visibleArea.height);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
MediaDocument::GetFileName(nsAString& aResult, nsIChannel* aChannel)
 | 
						|
{
 | 
						|
  aResult.Truncate();
 | 
						|
 | 
						|
  if (aChannel) {
 | 
						|
    aChannel->GetContentDispositionFilename(aResult);
 | 
						|
    if (!aResult.IsEmpty())
 | 
						|
      return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIURL> url = do_QueryInterface(mDocumentURI);
 | 
						|
  if (!url)
 | 
						|
    return;
 | 
						|
 | 
						|
  nsAutoCString fileName;
 | 
						|
  url->GetFileName(fileName);
 | 
						|
  if (fileName.IsEmpty())
 | 
						|
    return;
 | 
						|
 | 
						|
  nsAutoCString docCharset;
 | 
						|
  // Now that the charset is set in |StartDocumentLoad| to the charset of
 | 
						|
  // the document viewer instead of a bogus value ("ISO-8859-1" set in
 | 
						|
  // |nsDocument|'s ctor), the priority is given to the current charset. 
 | 
						|
  // This is necessary to deal with a media document being opened in a new 
 | 
						|
  // window or a new tab, in which case |originCharset| of |nsIURI| is not 
 | 
						|
  // reliable.
 | 
						|
  if (mCharacterSetSource != kCharsetUninitialized) {  
 | 
						|
    docCharset = mCharacterSet;
 | 
						|
  } else {  
 | 
						|
    // resort to |originCharset|
 | 
						|
    url->GetOriginCharset(docCharset);
 | 
						|
    SetDocumentCharacterSet(docCharset);
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv;
 | 
						|
  nsCOMPtr<nsITextToSubURI> textToSubURI = 
 | 
						|
    do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
 | 
						|
  if (NS_SUCCEEDED(rv)) {
 | 
						|
    // UnEscapeURIForUI always succeeds
 | 
						|
    textToSubURI->UnEscapeURIForUI(docCharset, fileName, aResult);
 | 
						|
  } else {
 | 
						|
    CopyUTF8toUTF16(fileName, aResult);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
MediaDocument::LinkStylesheet(const nsAString& aStylesheet)
 | 
						|
{
 | 
						|
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
 | 
						|
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::link, nullptr,
 | 
						|
                                           kNameSpaceID_XHTML,
 | 
						|
                                           nsIDOMNode::ELEMENT_NODE);
 | 
						|
 | 
						|
  RefPtr<nsGenericHTMLElement> link = NS_NewHTMLLinkElement(nodeInfo.forget());
 | 
						|
  NS_ENSURE_TRUE(link, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
 | 
						|
  link->SetAttr(kNameSpaceID_None, nsGkAtoms::rel, 
 | 
						|
                NS_LITERAL_STRING("stylesheet"), true);
 | 
						|
 | 
						|
  link->SetAttr(kNameSpaceID_None, nsGkAtoms::href, aStylesheet, true);
 | 
						|
 | 
						|
  Element* head = GetHeadElement();
 | 
						|
  return head->AppendChildTo(link, false);
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
MediaDocument::LinkScript(const nsAString& aScript)
 | 
						|
{
 | 
						|
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
 | 
						|
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::script, nullptr,
 | 
						|
                                           kNameSpaceID_XHTML,
 | 
						|
                                           nsIDOMNode::ELEMENT_NODE);
 | 
						|
 | 
						|
  RefPtr<nsGenericHTMLElement> script = NS_NewHTMLScriptElement(nodeInfo.forget());
 | 
						|
  NS_ENSURE_TRUE(script, NS_ERROR_OUT_OF_MEMORY);
 | 
						|
 | 
						|
  script->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
 | 
						|
                  NS_LITERAL_STRING("text/javascript;version=1.8"), true);
 | 
						|
 | 
						|
  script->SetAttr(kNameSpaceID_None, nsGkAtoms::src, aScript, true);
 | 
						|
 | 
						|
  Element* head = GetHeadElement();
 | 
						|
  return head->AppendChildTo(script, false);
 | 
						|
}
 | 
						|
 | 
						|
void 
 | 
						|
MediaDocument::UpdateTitleAndCharset(const nsACString& aTypeStr,
 | 
						|
                                     nsIChannel* aChannel,
 | 
						|
                                     const char* const* aFormatNames,
 | 
						|
                                     int32_t aWidth, int32_t aHeight,
 | 
						|
                                     const nsAString& aStatus)
 | 
						|
{
 | 
						|
  nsXPIDLString fileStr;
 | 
						|
  GetFileName(fileStr, aChannel);
 | 
						|
 | 
						|
  NS_ConvertASCIItoUTF16 typeStr(aTypeStr);
 | 
						|
  nsXPIDLString title;
 | 
						|
 | 
						|
  if (mStringBundle) {
 | 
						|
    // if we got a valid size (not all media have a size)
 | 
						|
    if (aWidth != 0 && aHeight != 0) {
 | 
						|
      nsAutoString widthStr;
 | 
						|
      nsAutoString heightStr;
 | 
						|
      widthStr.AppendInt(aWidth);
 | 
						|
      heightStr.AppendInt(aHeight);
 | 
						|
      // If we got a filename, display it
 | 
						|
      if (!fileStr.IsEmpty()) {
 | 
						|
        const char16_t *formatStrings[4]  = {fileStr.get(), typeStr.get(), 
 | 
						|
          widthStr.get(), heightStr.get()};
 | 
						|
        NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithDimAndFile]);
 | 
						|
        mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 4,
 | 
						|
                                            getter_Copies(title));
 | 
						|
      } 
 | 
						|
      else {
 | 
						|
        const char16_t *formatStrings[3]  = {typeStr.get(), widthStr.get(), 
 | 
						|
          heightStr.get()};
 | 
						|
        NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithDim]);
 | 
						|
        mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 3,
 | 
						|
                                            getter_Copies(title));
 | 
						|
      }
 | 
						|
    } 
 | 
						|
    else {
 | 
						|
    // If we got a filename, display it
 | 
						|
      if (!fileStr.IsEmpty()) {
 | 
						|
        const char16_t *formatStrings[2] = {fileStr.get(), typeStr.get()};
 | 
						|
        NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithFile]);
 | 
						|
        mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2,
 | 
						|
                                            getter_Copies(title));
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        const char16_t *formatStrings[1] = {typeStr.get()};
 | 
						|
        NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithNoInfo]);
 | 
						|
        mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 1,
 | 
						|
                                            getter_Copies(title));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } 
 | 
						|
 | 
						|
  // set it on the document
 | 
						|
  if (aStatus.IsEmpty()) {
 | 
						|
    SetTitle(title);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    nsXPIDLString titleWithStatus;
 | 
						|
    const nsPromiseFlatString& status = PromiseFlatString(aStatus);
 | 
						|
    const char16_t *formatStrings[2] = {title.get(), status.get()};
 | 
						|
    NS_NAMED_LITERAL_STRING(fmtName, "TitleWithStatus");
 | 
						|
    mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2,
 | 
						|
                                        getter_Copies(titleWithStatus));
 | 
						|
    SetTitle(titleWithStatus);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void 
 | 
						|
MediaDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject)
 | 
						|
{
 | 
						|
    nsHTMLDocument::SetScriptGlobalObject(aGlobalObject);
 | 
						|
    if (!mDocumentElementInserted && aGlobalObject) {
 | 
						|
        mDocumentElementInserted = true;
 | 
						|
        nsContentUtils::AddScriptRunner(
 | 
						|
            new nsDocElementCreatedNotificationRunner(this));        
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
} // namespace dom
 | 
						|
} // namespace mozilla
 |