Files
Mozilla/mozilla/content/html/document/src/nsMediaDocument.cpp
bzbarsky%mit.edu cc0346a98b When showing a document viewer, don't start layout on the documnt unless it's
already had layout started once.  Otherwise, just wait for the sink, or whoever
is responsible for it, to start layout once they're ready.  Bug 404470, r+sr=jst


git-svn-id: svn://10.0.0.236/trunk@243619 18797224-902f-48f8-a5cc-f745e15eee43
2008-01-20 18:02:03 +00:00

387 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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):
* Jungshik Shin <jshin@mailaps.org>
*
* 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 "nsMediaDocument.h"
#include "nsGkAtoms.h"
#include "nsRect.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsIScrollable.h"
#include "nsIViewManager.h"
#include "nsITextToSubURI.h"
#include "nsIURL.h"
#include "nsPrintfCString.h"
#include "nsIContentViewer.h"
#include "nsIMarkupDocumentViewer.h"
#include "nsIDocShell.h"
#include "nsIParser.h" // kCharsetFrom* macro definition
#include "nsIDocumentCharsetInfo.h"
#include "nsNodeInfoManager.h"
nsMediaDocumentStreamListener::nsMediaDocumentStreamListener(nsMediaDocument *aDocument)
{
mDocument = aDocument;
}
nsMediaDocumentStreamListener::~nsMediaDocumentStreamListener()
{
}
NS_IMPL_THREADSAFE_ISUPPORTS2(nsMediaDocumentStreamListener,
nsIRequestObserver,
nsIStreamListener)
void
nsMediaDocumentStreamListener::SetStreamListener(nsIStreamListener *aListener)
{
mNextStream = aListener;
}
NS_IMETHODIMP
nsMediaDocumentStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
{
NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
mDocument->StartLayout();
if (mNextStream) {
return mNextStream->OnStartRequest(request, ctxt);
}
return NS_OK;
}
NS_IMETHODIMP
nsMediaDocumentStreamListener::OnStopRequest(nsIRequest* request,
nsISupports *ctxt,
nsresult status)
{
nsresult rv = NS_OK;
if (mNextStream) {
rv = mNextStream->OnStopRequest(request, ctxt, status);
}
// No more need for our document so clear our reference and prevent leaks
mDocument = nsnull;
return rv;
}
NS_IMETHODIMP
nsMediaDocumentStreamListener::OnDataAvailable(nsIRequest* request,
nsISupports *ctxt,
nsIInputStream *inStr,
PRUint32 sourceOffset,
PRUint32 count)
{
if (mNextStream) {
return mNextStream->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
}
return NS_OK;
}
// default format names for nsMediaDocument.
const char* const nsMediaDocument::sFormatNames[4] =
{
"MediaTitleWithNoInfo", // eWithNoInfo
"MediaTitleWithFile", // eWithFile
"", // eWithDim
"" // eWithDimAndFile
};
nsMediaDocument::nsMediaDocument()
{
}
nsMediaDocument::~nsMediaDocument()
{
}
nsresult
nsMediaDocument::Init()
{
nsresult rv = nsHTMLDocument::Init();
NS_ENSURE_SUCCESS(rv, rv);
// Create a bundle for the localization
nsCOMPtr<nsIStringBundleService> stringService(
do_GetService(NS_STRINGBUNDLE_CONTRACTID));
if (stringService) {
stringService->CreateBundle(NSMEDIADOCUMENT_PROPERTIES_URI,
getter_AddRefs(mStringBundle));
}
return NS_OK;
}
nsresult
nsMediaDocument::StartDocumentLoad(const char* aCommand,
nsIChannel* aChannel,
nsILoadGroup* aLoadGroup,
nsISupports* aContainer,
nsIStreamListener** aDocListener,
PRBool 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.
// When this document is opened in the window/tab of the referring
// document (by a simple link-clicking), |prevDocCharacterSet| contains
// the charset of the referring document. On the other hand, if the
// document is opened in a new window, it is |defaultCharacterSet| of |muCV|
// where the charset of our interest is stored. In case of openining
// in a new tab, we get the charset from |documentCharsetInfo|. 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);
nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
nsCAutoString charset;
docShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
if (dcInfo) {
nsCOMPtr<nsIAtom> csAtom;
dcInfo->GetParentCharset(getter_AddRefs(csAtom));
if (csAtom) { // opening in a new tab
csAtom->ToUTF8String(charset);
}
}
if (charset.IsEmpty() || charset.Equals("UTF-8")) {
nsCOMPtr<nsIContentViewer> cv;
docShell->GetContentViewer(getter_AddRefs(cv));
// not being able to set the charset is not critical.
NS_ENSURE_TRUE(cv, NS_OK);
nsCOMPtr<nsIMarkupDocumentViewer> muCV = do_QueryInterface(cv);
if (muCV) {
muCV->GetPrevDocCharacterSet(charset); // opening in the same window/tab
if (charset.Equals("UTF-8") || charset.IsEmpty()) {
muCV->GetDefaultCharacterSet(charset); // opening in a new window
}
}
}
if (!charset.IsEmpty() && !charset.Equals("UTF-8")) {
SetDocumentCharacterSet(charset);
mCharacterSetSource = kCharsetFromUserDefault;
}
return NS_OK;
}
nsresult
nsMediaDocument::CreateSyntheticDocument()
{
// Synthesize an empty html document
nsresult rv;
nsCOMPtr<nsINodeInfo> nodeInfo;
rv = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nsnull,
kNameSpaceID_None,
getter_AddRefs(nodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsGenericHTMLElement> root = NS_NewHTMLHtmlElement(nodeInfo);
if (!root) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ASSERTION(GetChildCount() == 0, "Shouldn't have any kids");
rv = AppendChildTo(root, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = mNodeInfoManager->GetNodeInfo(nsGkAtoms::body, nsnull,
kNameSpaceID_None,
getter_AddRefs(nodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsGenericHTMLElement> body = NS_NewHTMLBodyElement(nodeInfo);
if (!body) {
return NS_ERROR_OUT_OF_MEMORY;
}
root->AppendChildTo(body, PR_FALSE);
return NS_OK;
}
nsresult
nsMediaDocument::StartLayout()
{
mMayStartLayout = PR_TRUE;
nsPresShellIterator iter(this);
nsCOMPtr<nsIPresShell> shell;
while ((shell = iter.GetNextShell())) {
nsRect visibleArea = shell->GetPresContext()->GetVisibleArea();
nsCOMPtr<nsIPresShell> shellGrip = shell;
nsresult rv = shell->InitialReflow(visibleArea.width, visibleArea.height);
NS_ENSURE_SUCCESS(rv, rv);
// Now trigger a refresh. vm might be null if the presshell got
// Destroy() called already.
nsIViewManager* vm = shell->GetViewManager();
if (vm) {
vm->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
}
}
return NS_OK;
}
void
nsMediaDocument::UpdateTitleAndCharset(const nsACString& aTypeStr,
const char* const* aFormatNames,
PRInt32 aWidth, PRInt32 aHeight,
const nsAString& aStatus)
{
nsXPIDLString fileStr;
if (mDocumentURI) {
nsCAutoString fileName;
nsCOMPtr<nsIURL> url = do_QueryInterface(mDocumentURI);
if (url)
url->GetFileName(fileName);
nsCAutoString 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|
mDocumentURI->GetOriginCharset(docCharset);
SetDocumentCharacterSet(docCharset);
}
if (!fileName.IsEmpty()) {
nsresult rv;
nsCOMPtr<nsITextToSubURI> textToSubURI =
do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
// UnEscapeURIForUI always succeeds
textToSubURI->UnEscapeURIForUI(docCharset, fileName, fileStr);
else
CopyUTF8toUTF16(fileName, fileStr);
}
}
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 PRUnichar *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 PRUnichar *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 PRUnichar *formatStrings[2] = {fileStr.get(), typeStr.get()};
NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithFile]);
mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2,
getter_Copies(title));
}
else {
const PRUnichar *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 PRUnichar *formatStrings[2] = {title.get(), status.get()};
NS_NAMED_LITERAL_STRING(fmtName, "TitleWithStatus");
mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2,
getter_Copies(titleWithStatus));
SetTitle(titleWithStatus);
}
}