Merge from mozilla-inbound.

This commit is contained in:
Jan de Mooij 2013-02-12 16:37:37 +01:00
commit 963211c18b
357 changed files with 2906 additions and 23810 deletions

View File

@ -329,14 +329,18 @@ bool
nsTextEquivUtils::AppendString(nsAString *aString,
const nsAString& aTextEquivalent)
{
// Insert spaces to insure that words from controls aren't jammed together.
if (aTextEquivalent.IsEmpty())
return false;
if (!aString->IsEmpty())
// Insert spaces to insure that words from controls aren't jammed together.
if (!aString->IsEmpty() && !IsWhitespace(aString->Last()))
aString->Append(PRUnichar(' '));
aString->Append(aTextEquivalent);
if (!IsWhitespace(aString->Last()))
aString->Append(PRUnichar(' '));
return true;
}

View File

@ -91,6 +91,9 @@
// Gets the name from html:input value, ignore @title attribute on input
testName("from_input_ignoretitle", "Custom country");
// Insert spaces around the control's value to not jamm sibling text nodes
testName("insert_spaces_around_control", "start value end");
// Gets the name from @title, ignore whitespace content
testName("from_label_ignore_ws_subtree", "about");
@ -262,6 +265,11 @@
title="ARIA slider and spinbutton don't provide a value for name computation">
Bug 812041
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=823927"
title="Text is jammed with control's text in name computation">
Bug 823927
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -389,6 +397,11 @@
value="Custom country"
title="Input your country of origin"/ >
<!-- name from subtree, surround control by spaces to not jamm the text -->
<label id="insert_spaces_around_control">
start<input value="value">end
</label>
<!-- no name from subtree because it holds whitespaces only -->
<a id="from_label_ignore_ws_subtree" href="about:" title="about">&nbsp;&nbsp; </a>

View File

@ -287,9 +287,6 @@
@BINPATH@/components/services-crypto.xpt
#endif
@BINPATH@/components/services-crypto-component.xpt
#ifdef MOZ_SERVICES_CAPTIVEDETECT
@BINPATH@/components/services-captivedetect.xpt
#endif
@BINPATH@/components/shellservice.xpt
@BINPATH@/components/shistory.xpt
@BINPATH@/components/spellchecker.xpt
@ -496,10 +493,6 @@
@BINPATH@/components/HealthReportComponents.manifest
@BINPATH@/components/HealthReportService.js
#endif
#ifdef MOZ_SERVICES_CAPTIVEDETECT
@BINPATH@/components/CaptivePortalDetectComponents.manifest
@BINPATH@/components/captivedetect.js
#endif
@BINPATH@/components/TelemetryPing.js
@BINPATH@/components/TelemetryPing.manifest
@BINPATH@/components/Webapps.js
@ -597,9 +590,6 @@
#ifdef MOZ_SERVICES_SYNC
@BINPATH@/defaults/pref/services-sync.js
#endif
#ifdef MOZ_SERVICES_CAPTIVEDETECT
@BINPATH@/defaults/pref/services-captivedetect.js
#endif
; [Layout Engine Resources]
; Style Sheets, Graphics and other Resources used by the layout engine.

View File

@ -18,10 +18,10 @@ function onLoad(event) {
gProgressBar = document.getElementById('uploadProgressBar');
if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
gProgressBar.style.display = "inline";
gProgressBar.hidden = false;
}
else {
gProgressBar.style.display = "none";
gProgressBar.hidden = true;
}
}

View File

@ -14,7 +14,8 @@ let gTests = [ {
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let progressBar = doc.getElementById("uploadProgressBar");
isnot(progressBar.style.display, "none", "progress bar should be visible");
let win = doc.defaultView;
isnot(win.getComputedStyle(progressBar).display, "none", "progress bar should be visible");
executeSoon(runNextTest);
}
},
@ -30,7 +31,8 @@ let gTests = [ {
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let progressBar = doc.getElementById("uploadProgressBar");
is(progressBar.style.display, "none",
let win = doc.defaultView;
is(win.getComputedStyle(progressBar).display, "none",
"progress bar should not be visible");
executeSoon(runNextTest);
}

View File

@ -286,9 +286,6 @@
@BINPATH@/components/saxparser.xpt
@BINPATH@/browser/components/sessionstore.xpt
@BINPATH@/components/services-crypto-component.xpt
#ifdef MOZ_SERVICES_CAPTIVEDETECT
@BINPATH@/components/services-captivedetect.xpt
#endif
@BINPATH@/browser/components/shellservice.xpt
@BINPATH@/components/shistory.xpt
@BINPATH@/components/spellchecker.xpt
@ -487,10 +484,6 @@
@BINPATH@/components/SyncComponents.manifest
@BINPATH@/components/Weave.js
#endif
#ifdef MOZ_SERVICES_CAPTIVEDETECT
@BINPATH@/components/CaptivePortalDetectComponents.manifest
@BINPATH@/components/captivedetect.js
#endif
@BINPATH@/components/servicesComponents.manifest
@BINPATH@/components/cryptoComponents.manifest
@BINPATH@/components/TelemetryPing.js
@ -573,10 +566,12 @@
#ifndef XP_MACOSX
; shell icons
@BINPATH@/browser/icons/*.png
#ifdef MOZ_UPDATER
; updater icon
@BINPATH@/icons/updater.png
#endif
#endif
#endif
; [Default Preferences]
; All the pref files must be part of base to prevent migration bugs

View File

@ -976,7 +976,6 @@ xpicleanup@BIN_SUFFIX@
defaults/pref/firefox.js
defaults/pref/firefox-l10n.js
defaults/pref/services-sync.js
defaults/pref/services-captivedetect.js
defaults/profile/bookmarks.html
defaults/profile/chrome/userChrome-example.css
defaults/profile/chrome/userContent-example.css

View File

@ -2174,6 +2174,10 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
.tab-throbber[progress] {
list-style-image: url("chrome://browser/skin/tabbrowser/loading@2x.png");
}
.tab-icon-image {
image-rendering: -moz-crisp-edges;
}
}
.tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([selected="true"]) {

View File

@ -442,6 +442,7 @@ user_pref("browser.startup.page", 0); // use about:blank, not browser.startup.ho
user_pref("browser.ui.layout.tablet", 0); // force tablet UI off
user_pref("dom.allow_scripts_to_close_windows", true);
user_pref("dom.disable_open_during_load", false);
user_pref("dom.experimental_forms", true); // on for testing
user_pref("dom.max_script_run_time", 0); // no slow script dialogs
user_pref("hangmonitor.timeout", 0); // no hang monitor
user_pref("dom.max_chrome_script_run_time", 0);

View File

@ -7,19 +7,26 @@ package @ANDROID_PACKAGE_NAME@;
import android.os.Bundle;
import android.test.InstrumentationTestRunner;
import android.util.Log;
public class FennecInstrumentationTestRunner extends InstrumentationTestRunner {
private static Bundle sArguments;
@Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
sArguments = arguments;
if (sArguments == null) {
Log.e("Robocop", "FennecInstrumentationTestRunner.onCreate got null bundle");
}
super.onCreate(arguments);
}
// unfortunately we have to make this static because test classes that don't extend
// from ActivityInstrumentationTestCase2 can't get a reference to this class.
public static Bundle getArguments() {
if (sArguments == null) {
Log.e("Robocop", "FennecInstrumentationTestCase.getArguments returns null bundle");
}
return sArguments;
}
}

View File

@ -14,6 +14,10 @@
// No throwing exceptions
#undef _STLP_NO_EXCEPTIONS
#define _STLP_NO_EXCEPTIONS 1
#undef _STLP_NO_EXCEPTION_HEADER
#define _STLP_NO_EXCEPTION_HEADER 1
#undef _STLP_NO_UNCAUGHT_EXCEPT_SUPPORT
#define _STLP_NO_UNCAUGHT_EXCEPT_SUPPORT 1
#undef _STLP_NATIVE_CPP_C_HEADER
#define _STLP_NATIVE_CPP_C_HEADER(header) <../../system/include/header>

View File

@ -4232,7 +4232,6 @@ MOZ_SAFE_BROWSING=
MOZ_HELP_VIEWER=
MOZ_SPELLCHECK=1
MOZ_ANDROID_OMTC=
MOZ_ANDROID_ANR_REPORTER=
MOZ_ONLY_TOUCH_EVENTS=
MOZ_TOOLKIT_SEARCH=1
MOZ_UI_LOCALE=en-US
@ -5087,13 +5086,6 @@ if test -n "$MOZ_ANDROID_OMTC"; then
AC_DEFINE(MOZ_ANDROID_OMTC)
fi
dnl ========================================================
dnl = Android App Not Responding (ANR) reporter
dnl ========================================================
if test -n "$MOZ_ANDROID_ANR_REPORTER"; then
AC_DEFINE(MOZ_ANDROID_ANR_REPORTER)
fi
dnl ========================================================
dnl = Disable WebSMS backend
dnl ========================================================
@ -8388,12 +8380,6 @@ if test -n "$MOZ_SERVICES_SYNC"; then
AC_DEFINE(MOZ_SERVICES_SYNC)
fi
dnl Build Captive Portal Detector if required
AC_SUBST(MOZ_SERVICES_CAPTIVEDETECT)
if test -n "$MOZ_SERVICES_CAPTIVEDETECT"; then
AC_DEFINE(MOZ_SERVICES_CAPTIVEDETECT)
fi
dnl ========================================================
if test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC" -o "$MOZ_DMD"; then
MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=
@ -8476,7 +8462,6 @@ AC_SUBST(MOZ_UNIVERSALCHARDET)
AC_SUBST(ACCESSIBILITY)
AC_SUBST(MOZ_SPELLCHECK)
AC_SUBST(MOZ_ANDROID_OMTC)
AC_SUBST(MOZ_ANDROID_ANR_REPORTER)
AC_SUBST(MOZ_ONLY_TOUCH_EVENTS)
AC_SUBST(MOZ_CRASHREPORTER)
AC_SUBST(MOZ_CRASHREPORTER_INJECTOR)

View File

@ -1133,8 +1133,6 @@ nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope)
JS_SetVersion(cx, JSVERSION_LATEST);
JS_SetErrorReporter(cx, ContentScriptErrorReporter);
xpc_LocalizeContext(cx);
JSAutoRequest ar(cx);
nsIXPConnect* xpc = nsContentUtils::XPConnect();
const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;

View File

@ -213,7 +213,8 @@ IsScopedStyleElement(nsIContent* aContent)
// This is quicker than, say, QIing aContent to nsStyleLinkElement
// and then calling its virtual GetStyleSheetInfo method to find out
// if it is scoped.
return aContent->IsHTML(nsGkAtoms::style) &&
return (aContent->IsHTML(nsGkAtoms::style) ||
aContent->IsSVG(nsGkAtoms::style)) &&
aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scoped);
}

View File

@ -34,8 +34,8 @@ XPCSHELL_TESTS += unit_ipc
endif
# Split files arbitrarily in two groups to not run into too-long command lines
# which break on Windows (see bug 563151)
# Split files arbitrarily in three groups to not run into too-long command lines
# which break on Windows (see bug 563151 and bug 831989)
MOCHITEST_FILES_A = \
responseIdentical.sjs \
test_bug5141.html \
@ -508,6 +508,9 @@ MOCHITEST_FILES_B = \
accesscontrol.resource^headers^ \
invalid_accesscontrol.resource \
invalid_accesscontrol.resource^headers^ \
$(NULL)
MOCHITEST_FILES_C= \
test_xhr_progressevents.html \
progressserver.sjs \
somedatas.resource \
@ -655,15 +658,16 @@ MOCHITEST_CHROME_FILES = \
test_bug357450.js \
$(NULL)
MOCHITEST_FILES_PARTS = $(foreach s,A B,MOCHITEST_FILES_$(s))
# This test fails on the Mac for some reason
ifneq (,$(filter gtk2 windows,$(MOZ_WIDGET_TOOLKIT)))
MOCHITEST_FILES_C = test_copyimage.html \
MOCHITEST_FILES_C += \
test_copyimage.html \
$(NULL)
MOCHITEST_FILES_PARTS += MOCHITEST_FILES_C
endif
MOCHITEST_FILES_PARTS = $(foreach s,A B C,MOCHITEST_FILES_$(s))
# Disabled for now. Mochitest isn't reliable enough for these.
# test_bug444546.html \
# bug444546.sjs \

View File

@ -3377,6 +3377,20 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
return NS_ERROR_DOM_SYNTAX_ERR;
}
IntRect srcRect(0, 0, mWidth, mHeight);
IntRect destRect(aX, aY, aWidth, aHeight);
IntRect srcReadRect = srcRect.Intersect(destRect);
RefPtr<DataSourceSurface> readback;
if (!srcReadRect.IsEmpty() && !mZero) {
RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
if (snapshot) {
readback = snapshot->GetDataSurface();
}
if (!readback || !readback->GetData()) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
JSObject* darray = JS_NewUint8ClampedArray(aCx, len.value());
if (!darray) {
return NS_ERROR_OUT_OF_MEMORY;
@ -3389,25 +3403,14 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
uint8_t* data = JS_GetUint8ClampedArrayData(darray);
IntRect srcRect(0, 0, mWidth, mHeight);
IntRect destRect(aX, aY, aWidth, aHeight);
IntRect srcReadRect = srcRect.Intersect(destRect);
IntRect dstWriteRect = srcReadRect;
dstWriteRect.MoveBy(-aX, -aY);
uint8_t* src = data;
uint32_t srcStride = aWidth * 4;
RefPtr<DataSourceSurface> readback;
if (!srcReadRect.IsEmpty()) {
RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
if (snapshot) {
readback = snapshot->GetDataSurface();
srcStride = readback->Stride();
src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
}
if (readback) {
srcStride = readback->Stride();
src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
}
// NOTE! dst is the same as src, and this relies on reading

View File

@ -108,6 +108,10 @@ public:
bool aNullParent = true);
virtual void DoneCreatingElement();
virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable,
int32_t *aTabIndex);
virtual int32_t TabIndexDefault();
/**
* Call this to reevaluate whether we should start/stop due to our owner
* document being active, inactive, visible or hidden.

View File

@ -0,0 +1,159 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/dom/HTMLProgressElement.h"
#include "mozilla/dom/HTMLProgressElementBinding.h"
NS_IMPL_NS_NEW_HTML_ELEMENT(Progress)
DOMCI_NODE_DATA(HTMLProgressElement, mozilla::dom::HTMLProgressElement)
namespace mozilla {
namespace dom {
const double HTMLProgressElement::kIndeterminatePosition = -1.0;
const double HTMLProgressElement::kDefaultValue = 0.0;
const double HTMLProgressElement::kDefaultMax = 1.0;
HTMLProgressElement::HTMLProgressElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
{
SetIsDOMBinding();
// We start out indeterminate
AddStatesSilently(NS_EVENT_STATE_INDETERMINATE);
}
HTMLProgressElement::~HTMLProgressElement()
{
}
NS_IMPL_ADDREF_INHERITED(HTMLProgressElement, Element)
NS_IMPL_RELEASE_INHERITED(HTMLProgressElement, Element)
NS_INTERFACE_TABLE_HEAD(HTMLProgressElement)
NS_HTML_CONTENT_INTERFACE_TABLE1(HTMLProgressElement,
nsIDOMHTMLProgressElement)
NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(HTMLProgressElement,
nsGenericHTMLElement)
NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLProgressElement)
NS_IMPL_ELEMENT_CLONE(HTMLProgressElement)
nsEventStates
HTMLProgressElement::IntrinsicState() const
{
nsEventStates state = nsGenericHTMLElement::IntrinsicState();
if (IsIndeterminate()) {
state |= NS_EVENT_STATE_INDETERMINATE;
}
return state;
}
bool
HTMLProgressElement::ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute,
const nsAString& aValue, nsAttrValue& aResult)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max) {
return aResult.ParseDoubleValue(aValue);
}
}
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute,
aValue, aResult);
}
NS_IMETHODIMP
HTMLProgressElement::GetValue(double* aValue)
{
*aValue = Value();
return NS_OK;
}
double
HTMLProgressElement::Value() const
{
const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(nsGkAtoms::value);
if (!attrValue || attrValue->Type() != nsAttrValue::eDoubleValue ||
attrValue->GetDoubleValue() < 0.0) {
return kDefaultValue;
}
return std::min(attrValue->GetDoubleValue(), Max());
}
NS_IMETHODIMP
HTMLProgressElement::SetValue(double aValue)
{
ErrorResult rv;
SetValue(aValue, rv);
return rv.ErrorCode();
}
NS_IMETHODIMP
HTMLProgressElement::GetMax(double* aValue)
{
*aValue = Max();
return NS_OK;
}
double
HTMLProgressElement::Max() const
{
const nsAttrValue* attrMax = mAttrsAndChildren.GetAttr(nsGkAtoms::max);
if (!attrMax || attrMax->Type() != nsAttrValue::eDoubleValue ||
attrMax->GetDoubleValue() <= 0.0) {
return kDefaultMax;
}
return attrMax->GetDoubleValue();
}
NS_IMETHODIMP
HTMLProgressElement::SetMax(double aValue)
{
ErrorResult rv;
SetMax(aValue, rv);
return rv.ErrorCode();
}
NS_IMETHODIMP
HTMLProgressElement::GetPosition(double* aPosition)
{
*aPosition = Position();
return NS_OK;
}
double
HTMLProgressElement::Position() const
{
if (IsIndeterminate()) {
return kIndeterminatePosition;
}
return Value() / Max();
}
bool
HTMLProgressElement::IsIndeterminate() const
{
const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(nsGkAtoms::value);
return !attrValue || attrValue->Type() != nsAttrValue::eDoubleValue;
}
JSObject*
HTMLProgressElement::WrapNode(JSContext* aCx, JSObject* aScope,
bool* aTriedToWrap)
{
return HTMLProgressElementBinding::Wrap(aCx, aScope, this, aTriedToWrap);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_dom_HTMLProgressElement_h
#define mozilla_dom_HTMLProgressElement_h
#include "nsIDOMHTMLProgressElement.h"
#include "nsGenericHTMLElement.h"
#include "nsAttrValue.h"
#include "nsAttrValueInlines.h"
#include "nsEventStateManager.h"
#include <algorithm>
namespace mozilla {
namespace dom {
class HTMLProgressElement : public nsGenericHTMLElement,
public nsIDOMHTMLProgressElement
{
public:
HTMLProgressElement(already_AddRefed<nsINodeInfo> aNodeInfo);
virtual ~HTMLProgressElement();
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIDOMNode
NS_FORWARD_NSIDOMNODE_TO_NSINODE
// nsIDOMElement
NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
// nsIDOMHTMLElement
NS_FORWARD_NSIDOMHTMLELEMENT_TO_GENERIC
// nsIDOMHTMLProgressElement
NS_DECL_NSIDOMHTMLPROGRESSELEMENT
nsEventStates IntrinsicState() const;
nsresult Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const;
bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute,
const nsAString& aValue, nsAttrValue& aResult);
virtual nsXPCClassInfo* GetClassInfo();
virtual nsIDOMNode* AsDOMNode() { return this; }
// WebIDL
double Value() const;
void SetValue(double aValue, ErrorResult& aRv)
{
aRv = SetDoubleAttr(nsGkAtoms::value, aValue);
}
double Max() const;
void SetMax(double aValue, ErrorResult& aRv)
{
aRv = SetDoubleAttr(nsGkAtoms::max, aValue);
}
double Position() const;
protected:
virtual JSObject* WrapNode(JSContext* aCx, JSObject* aScope,
bool* aTriedToWrap) MOZ_OVERRIDE;
protected:
/**
* Returns whethem the progress element is in the indeterminate state.
* A progress element is in the indeterminate state if its value is ommited
* or is not a floating point number..
*
* @return whether the progress element is in the indeterminate state.
*/
bool IsIndeterminate() const;
static const double kIndeterminatePosition;
static const double kDefaultValue;
static const double kDefaultMax;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_HTMLProgressElement_h

View File

@ -53,6 +53,7 @@ EXPORTS_mozilla/dom = \
HTMLOptGroupElement.h \
HTMLParagraphElement.h \
HTMLPreElement.h \
HTMLProgressElement.h \
HTMLScriptElement.h \
HTMLSharedElement.h \
HTMLSharedListElement.h \
@ -114,7 +115,7 @@ CPPSRCS = \
nsHTMLOutputElement.cpp \
HTMLParagraphElement.cpp \
HTMLPreElement.cpp \
nsHTMLProgressElement.cpp \
HTMLProgressElement.cpp \
HTMLScriptElement.cpp \
nsHTMLSelectElement.cpp \
HTMLSharedElement.cpp \

View File

@ -358,8 +358,8 @@ public:
* in aIsFocusable.
*/
virtual bool IsHTMLFocusable(bool aWithMouse,
bool *aIsFocusable,
int32_t *aTabIndex);
bool *aIsFocusable,
int32_t *aTabIndex);
virtual void PerformAccesskey(bool aKeyCausesActivation,
bool aIsTrustedEvent);

View File

@ -2048,6 +2048,23 @@ void nsHTMLMediaElement::DoneCreatingElement()
mMuted = true;
}
bool nsHTMLMediaElement::IsHTMLFocusable(bool aWithMouse,
bool *aIsFocusable,
int32_t *aTabIndex)
{
if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
return true;
}
*aIsFocusable = true;
return false;
}
int32_t nsHTMLMediaElement::TabIndexDefault()
{
return 0;
}
nsresult nsHTMLMediaElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)

View File

@ -1,192 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsIDOMHTMLProgressElement.h"
#include "nsGenericHTMLElement.h"
#include "nsAttrValue.h"
#include "nsAttrValueInlines.h"
#include "nsEventStateManager.h"
#include <algorithm>
using namespace mozilla::dom;
class nsHTMLProgressElement : public nsGenericHTMLElement,
public nsIDOMHTMLProgressElement
{
public:
nsHTMLProgressElement(already_AddRefed<nsINodeInfo> aNodeInfo);
virtual ~nsHTMLProgressElement();
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIDOMNode
NS_FORWARD_NSIDOMNODE_TO_NSINODE
// nsIDOMElement
NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
// nsIDOMHTMLElement
NS_FORWARD_NSIDOMHTMLELEMENT_TO_GENERIC
// nsIDOMHTMLProgressElement
NS_DECL_NSIDOMHTMLPROGRESSELEMENT
nsEventStates IntrinsicState() const;
nsresult Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const;
bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute,
const nsAString& aValue, nsAttrValue& aResult);
virtual nsXPCClassInfo* GetClassInfo();
virtual nsIDOMNode* AsDOMNode() { return this; }
protected:
/**
* Returns whethem the progress element is in the indeterminate state.
* A progress element is in the indeterminate state if its value is ommited
* or is not a floating point number..
*
* @return whether the progress element is in the indeterminate state.
*/
bool IsIndeterminate() const;
static const double kIndeterminatePosition;
static const double kDefaultValue;
static const double kDefaultMax;
};
const double nsHTMLProgressElement::kIndeterminatePosition = -1.0;
const double nsHTMLProgressElement::kDefaultValue = 0.0;
const double nsHTMLProgressElement::kDefaultMax = 1.0;
NS_IMPL_NS_NEW_HTML_ELEMENT(Progress)
nsHTMLProgressElement::nsHTMLProgressElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
{
// We start out indeterminate
AddStatesSilently(NS_EVENT_STATE_INDETERMINATE);
}
nsHTMLProgressElement::~nsHTMLProgressElement()
{
}
NS_IMPL_ADDREF_INHERITED(nsHTMLProgressElement, Element)
NS_IMPL_RELEASE_INHERITED(nsHTMLProgressElement, Element)
DOMCI_NODE_DATA(HTMLProgressElement, nsHTMLProgressElement)
NS_INTERFACE_TABLE_HEAD(nsHTMLProgressElement)
NS_HTML_CONTENT_INTERFACE_TABLE1(nsHTMLProgressElement,
nsIDOMHTMLProgressElement)
NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLProgressElement,
nsGenericHTMLElement)
NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLProgressElement)
NS_IMPL_ELEMENT_CLONE(nsHTMLProgressElement)
nsEventStates
nsHTMLProgressElement::IntrinsicState() const
{
nsEventStates state = nsGenericHTMLElement::IntrinsicState();
if (IsIndeterminate()) {
state |= NS_EVENT_STATE_INDETERMINATE;
}
return state;
}
bool
nsHTMLProgressElement::ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute,
const nsAString& aValue, nsAttrValue& aResult)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max) {
return aResult.ParseDoubleValue(aValue);
}
}
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute,
aValue, aResult);
}
NS_IMETHODIMP
nsHTMLProgressElement::GetValue(double* aValue)
{
const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(nsGkAtoms::value);
if (!attrValue || attrValue->Type() != nsAttrValue::eDoubleValue ||
attrValue->GetDoubleValue() < 0.0) {
*aValue = kDefaultValue;
return NS_OK;
}
*aValue = attrValue->GetDoubleValue();
double max;
GetMax(&max);
*aValue = std::min(*aValue, max);
return NS_OK;
}
NS_IMETHODIMP
nsHTMLProgressElement::SetValue(double aValue)
{
return SetDoubleAttr(nsGkAtoms::value, aValue);
}
NS_IMETHODIMP
nsHTMLProgressElement::GetMax(double* aValue)
{
const nsAttrValue* attrMax = mAttrsAndChildren.GetAttr(nsGkAtoms::max);
if (attrMax && attrMax->Type() == nsAttrValue::eDoubleValue &&
attrMax->GetDoubleValue() > 0.0) {
*aValue = attrMax->GetDoubleValue();
} else {
*aValue = kDefaultMax;
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLProgressElement::SetMax(double aValue)
{
return SetDoubleAttr(nsGkAtoms::max, aValue);
}
NS_IMETHODIMP
nsHTMLProgressElement::GetPosition(double* aPosition)
{
if (IsIndeterminate()) {
*aPosition = kIndeterminatePosition;
return NS_OK;
}
double value;
double max;
GetValue(&value);
GetMax(&max);
*aPosition = value / max;
return NS_OK;
}
bool
nsHTMLProgressElement::IsIndeterminate() const
{
const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(nsGkAtoms::value);
return !attrValue || attrValue->Type() != nsAttrValue::eDoubleValue;
}

View File

@ -17,8 +17,6 @@
// TODO: maybe make those reflections be tested against all input types.
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
// .accept
reflectString({
element: document.createElement("input"),
@ -246,9 +244,6 @@ reflectUnsignedInt({
// .validationMessage doesn't reflect a content attribute.
// .labels doesn't reflect a content attribute.
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -308,8 +308,6 @@ function checkSanitizing(element)
}
}
var pref = SpecialPowers.getBoolPref("dom.experimental_forms");
SpecialPowers.setBoolPref("dom.experimental_forms", true);
for (type of inputTypes) {
var form = document.forms[0];
var element = document.createElement("input");
@ -345,8 +343,6 @@ for (type of todoTypes) {
form.removeChild(element);
}
SpecialPowers.setBoolPref("dom.experimental_forms", pref);
</script>
</pre>
</body>

View File

@ -213,11 +213,7 @@ function runTest()
var testRunner = runTest();
addLoadEvent(function () {
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]},
function() {
testRunner.next();
}
);
testRunner.next();
});
</script>

View File

@ -77,8 +77,6 @@ function checkValidity(aElement, aValidity, aApply, aRangeApply)
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
for (var test of data) {
input.type = test.type;
var apply = test.apply;
@ -288,9 +286,6 @@ for (var test of data) {
input.value = '';
}
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -77,8 +77,6 @@ function checkValidity(aElement, aValidity, aApply, aRangeApply)
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
for (var test of data) {
input.type = test.type;
var apply = test.apply;
@ -294,9 +292,6 @@ for (var test of data) {
input.value = '';
}
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -102,8 +102,6 @@ function checkMozIsTextFieldValueTodo(aInput, aResult)
"mozIsTextField(true) should return " + aResult);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
// Check if the method is defined for the correct elements.
for (data of gElementTestData) {
checkMozIsTextFieldDefined(data[0], data[1]);
@ -122,9 +120,6 @@ for (data of gInputTodoData) {
checkMozIsTextFieldValueTodo(input, data[1]);
}
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -265,8 +265,6 @@ var invalidTypes = Array('checkbox', 'radio', 'file', 'number', 'date', 'time');
// TODO: 'datetime', 'month', 'week', 'datetime-local', 'range' and 'color'
// do not accept the @pattern too but are not implemented yet.
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
for (type of validTypes) {
input.type = type;
completeValidityCheck(input, false);
@ -283,9 +281,6 @@ for (type of invalidTypes) {
completeValidityCheck(input, true);
}
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -356,9 +356,6 @@ function checkInputRequiredValidityForFile()
checkNotSufferingFromBeingMissing(element);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkTextareaRequiredValidity();
// The require attribute behavior depend of the input type.
@ -390,9 +387,6 @@ checkInputRequiredValidityForCheckbox();
checkInputRequiredValidityForRadio();
checkInputRequiredValidityForFile();
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -83,8 +83,6 @@ function checkValidity(aElement, aValidity, aApply, aData)
(aElement.wil && aValidity) ? ":invalid shouldn't apply" : "valid should apply");
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
for (var test of data) {
var input = getFreshElement(test.type);
var apply = test.apply;
@ -523,9 +521,6 @@ for (var test of data) {
}
}
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -601,18 +601,12 @@ function checkStepUp()
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkPresence();
checkAvailability();
checkStepDown();
checkStepUp();
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -285,8 +285,6 @@ function checkValidityStateObjectAliveWithoutElement(element)
ok(v.valid, "When the element is not alive, it should be valid");
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkConstraintValidationAPIExist(document.getElementById('f'));
checkConstraintValidationAPIExist(document.getElementById('i'));
checkConstraintValidationAPIExist(document.getElementById('b'));
@ -341,9 +339,6 @@ checkValidityStateObjectAliveWithoutElement("textarea");
checkValidityStateObjectAliveWithoutElement("output");
checkValidityStateObjectAliveWithoutElement("object");
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -372,9 +372,6 @@ function checkWithBustedPrototype()
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkAvailability();
checkGarbageValues();
checkWithBustedPrototype();
@ -387,9 +384,6 @@ checkDateSet();
checkTimeGet();
checkTimeSet();
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -396,8 +396,6 @@ function checkTimeSet()
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkAvailability();
// <input type='number'> test
@ -412,9 +410,6 @@ checkDateSet();
checkTimeGet();
checkTimeSet();
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -46,9 +46,6 @@ var todoTypes = [
var nonTrivialSanitizing = [ 'number', 'date', 'time' ];
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
var length = testData.length;
for (var i=0; i<length; ++i) {
for (var j=0; j<length; ++j) {
@ -101,9 +98,6 @@ for (var type of todoTypes) {
todo_is(e.type, type, type + " type isn't supported yet");
}
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@ -52,6 +52,8 @@
namespace mozilla {
using namespace dom;
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaDecoderLog;
#endif

View File

@ -1177,6 +1177,9 @@ this.DOMApplicationRegistry = {
? updateSvc.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
: updateSvc.scheduleAppUpdate(appcacheURI, docURI, aApp.localId, false);
// initialize the progress to 0 right now
aApp.progress = 0;
// We save the download details for potential further usage like cancelling
// it.
let download = {
@ -2050,6 +2053,13 @@ this.DOMApplicationRegistry = {
AppDownloadManager.remove(aApp.manifestURL);
}
function sendProgressEvent() {
self.broadcastMessage("Webapps:PackageEvent",
{ type: "progress",
manifestURL: aApp.manifestURL,
app: app });
}
function download() {
debug("About to download " + aManifest.fullPackagePath());
@ -2070,6 +2080,7 @@ this.DOMApplicationRegistry = {
);
let lastProgressTime = 0;
requestChannel.notificationCallbacks = {
QueryInterface: function notifQI(aIID) {
if (aIID.equals(Ci.nsISupports) ||
@ -2088,10 +2099,7 @@ this.DOMApplicationRegistry = {
let now = Date.now();
if (now - lastProgressTime > MIN_PROGRESS_EVENT_DELAY) {
debug("onProgress: " + aProgress + "/" + aProgressMax);
self.broadcastMessage("Webapps:PackageEvent",
{ type: "progress",
manifestURL: aApp.manifestURL,
app: app });
sendProgressEvent();
lastProgressTime = now;
self._saveApps();
}
@ -2117,6 +2125,9 @@ this.DOMApplicationRegistry = {
// installed apps should morph to 'updating'.
app.installState = aIsUpdate ? "updating" : "pending";
// initialize the progress to 0 right now
app.progress = 0;
// Staging the zip in TmpD until all the checks are done.
let zipFile = FileUtils.getFile("TmpD",
["webapps", id, "application.zip"], true);
@ -2327,6 +2338,9 @@ this.DOMApplicationRegistry = {
});
requestChannel.asyncOpen(listener, null);
// send a first progress event to correctly set the DOM object's properties
sendProgressEvent();
};
let deviceStorage = Services.wm.getMostRecentWindow("navigator:browser")
@ -2810,18 +2824,30 @@ let AppcacheObserver = function(aApp) {
this.app = aApp;
this.startStatus = aApp.installState;
this.lastProgressTime = 0;
// send a first progress event to correctly set the DOM object's properties
this._sendProgressEvent();
};
AppcacheObserver.prototype = {
// nsIOfflineCacheUpdateObserver implementation
_sendProgressEvent: function() {
let app = this.app;
DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
{ manifest: app.manifestURL,
installState: app.installState,
progress: app.progress });
},
updateStateChanged: function appObs_Update(aUpdate, aState) {
let mustSave = false;
let app = this.app;
debug("Offline cache state change for " + app.origin + " : " + aState);
var self = this;
let setStatus = function appObs_setStatus(aStatus, aProgress) {
debug("Offlinecache setStatus to " + aStatus + " for " + app.origin);
debug("Offlinecache setStatus to " + aStatus + " with progress " +
aProgress + " for " + app.origin);
mustSave = (app.installState != aStatus);
app.installState = aStatus;
app.progress = aProgress;
@ -2829,10 +2855,7 @@ AppcacheObserver.prototype = {
app.downloading = false;
app.downloadAvailable = false;
}
DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
{ manifest: app.manifestURL,
installState: app.installState,
progress: app.progress });
self._sendProgressEvent();
}
let setError = function appObs_setError(aError) {

View File

@ -1125,8 +1125,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime, bool aGCOnDestruction,
js_options_dot_str, this);
::JS_SetOperationCallback(mContext, DOMOperationCallback);
xpc_LocalizeContext(mContext);
}
mIsInitialized = false;
mTerminations = nullptr;

View File

@ -506,6 +506,10 @@ DOMInterfaces = {
'hasInstanceInterface': 'nsIDOMHTMLPreElement',
},
'HTMLProgressElement': {
'hasInstanceInterface': 'nsIDOMHTMLProgressElement',
},
'HTMLPropertiesCollection': {
'headerFile': 'HTMLPropertiesCollection.h',
'resultNotAddRefed': [ 'item', 'namedItem', 'names' ]

View File

@ -22,10 +22,7 @@ ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
TEST_DIRS += tests
endif
EXPORTS = \
nsICachedFileDescriptorListener.h \
PCOMContentPermissionRequestChild.h \
$(NULL)
EXPORTS = PCOMContentPermissionRequestChild.h
EXPORTS_NAMESPACES = \
mozilla \

View File

@ -31,7 +31,6 @@ using gfxSize;
using mozilla::layers::LayersBackend;
using mozilla::layers::FrameMetrics;
using mozilla::layout::ScrollingBehavior;
using mozilla::void_t;
using mozilla::WindowsHandle;
using nscolor;
using nsCompositionEvent;
@ -291,8 +290,6 @@ child:
LoadURL(nsCString uri);
CacheFileDescriptor(nsString path, FileDescriptor fd);
UpdateDimensions(nsRect rect, nsIntSize size, ScreenOrientation orientation) compress;
UpdateFrame(FrameMetrics frame) compress;

View File

@ -18,7 +18,6 @@
#include "mozilla/dom/PContentChild.h"
#include "mozilla/dom/PContentDialogChild.h"
#include "mozilla/ipc/DocumentRendererChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/layers/AsyncPanZoomController.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/PLayersChild.h"
@ -38,7 +37,6 @@
#include "mozilla/dom/Element.h"
#include "nsIAppsService.h"
#include "nsIBaseWindow.h"
#include "nsICachedFileDescriptorListener.h"
#include "nsIComponentManager.h"
#include "nsIDocumentInlines.h"
#include "nsIDOMClassInfo.h"
@ -123,98 +121,6 @@ public:
const InfallibleTArray<nsString>& aStringParams);
};
class TabChild::CachedFileDescriptorInfo
{
struct PathOnlyComparatorHelper
{
bool Equals(const nsAutoPtr<CachedFileDescriptorInfo>& a,
const CachedFileDescriptorInfo& b) const
{
return a->mPath == b.mPath;
}
};
struct PathAndCallbackComparatorHelper
{
bool Equals(const nsAutoPtr<CachedFileDescriptorInfo>& a,
const CachedFileDescriptorInfo& b) const
{
return a->mPath == b.mPath &&
a->mCallback == b.mCallback;
}
};
public:
nsString mPath;
FileDescriptor mFileDescriptor;
nsCOMPtr<nsICachedFileDescriptorListener> mCallback;
bool mCanceled;
CachedFileDescriptorInfo(const nsAString& aPath)
: mPath(aPath), mCanceled(false)
{ }
CachedFileDescriptorInfo(const nsAString& aPath,
const FileDescriptor& aFileDescriptor)
: mPath(aPath), mFileDescriptor(aFileDescriptor), mCanceled(false)
{ }
CachedFileDescriptorInfo(const nsAString& aPath,
nsICachedFileDescriptorListener* aCallback)
: mPath(aPath), mCallback(aCallback), mCanceled(false)
{ }
PathOnlyComparatorHelper PathOnlyComparator() const
{
return PathOnlyComparatorHelper();
}
PathAndCallbackComparatorHelper PathAndCallbackComparator() const
{
return PathAndCallbackComparatorHelper();
}
void FireCallback() const
{
mCallback->OnCachedFileDescriptor(mPath, mFileDescriptor);
}
};
class TabChild::CachedFileDescriptorCallbackRunnable : public nsRunnable
{
typedef TabChild::CachedFileDescriptorInfo CachedFileDescriptorInfo;
nsAutoPtr<CachedFileDescriptorInfo> mInfo;
public:
CachedFileDescriptorCallbackRunnable(CachedFileDescriptorInfo* aInfo)
: mInfo(aInfo)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aInfo);
MOZ_ASSERT(!aInfo->mPath.IsEmpty());
MOZ_ASSERT(aInfo->mCallback);
}
void Dispatch()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = NS_DispatchToCurrentThread(this);
NS_ENSURE_SUCCESS_VOID(rv);
}
private:
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInfo);
mInfo->FireCallback();
return NS_OK;
}
};
StaticRefPtr<TabChild> sPreallocatedTab;
/*static*/ void
@ -1211,130 +1117,6 @@ TabChild::RecvLoadURL(const nsCString& uri)
return true;
}
bool
TabChild::RecvCacheFileDescriptor(const nsString& aPath,
const FileDescriptor& aFileDescriptor)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aPath.IsEmpty());
// aFileDescriptor may be invalid here, but the callback will choose how to
// handle it.
// First see if we already have a request for this path.
const CachedFileDescriptorInfo search(aPath);
uint32_t index =
mCachedFileDescriptorInfos.IndexOf(search, 0,
search.PathOnlyComparator());
if (index == mCachedFileDescriptorInfos.NoIndex) {
// We haven't had any requests for this path yet. Assume that we will
// in a little while and save the file descriptor here.
mCachedFileDescriptorInfos.AppendElement(
new CachedFileDescriptorInfo(aPath, aFileDescriptor));
return true;
}
nsAutoPtr<CachedFileDescriptorInfo>& info =
mCachedFileDescriptorInfos[index];
MOZ_ASSERT(info);
MOZ_ASSERT(info->mPath == aPath);
MOZ_ASSERT(!info->mFileDescriptor.IsValid());
MOZ_ASSERT(info->mCallback);
// If this callback has been canceled then we can simply close the file
// descriptor and forget about the callback.
if (info->mCanceled) {
// Only close if this is a valid file descriptor.
if (aFileDescriptor.IsValid()) {
nsRefPtr<CloseFileRunnable> runnable =
new CloseFileRunnable(aFileDescriptor);
runnable->Dispatch();
}
} else {
// Not canceled so fire the callback.
info->mFileDescriptor = aFileDescriptor;
// We don't need a runnable here because we should already be at the top
// of the event loop. Just fire immediately.
info->FireCallback();
}
mCachedFileDescriptorInfos.RemoveElementAt(index);
return true;
}
bool
TabChild::GetCachedFileDescriptor(const nsAString& aPath,
nsICachedFileDescriptorListener* aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aPath.IsEmpty());
MOZ_ASSERT(aCallback);
// First see if we've already received a cached file descriptor for this
// path.
const CachedFileDescriptorInfo search(aPath);
uint32_t index =
mCachedFileDescriptorInfos.IndexOf(search, 0,
search.PathOnlyComparator());
if (index == mCachedFileDescriptorInfos.NoIndex) {
// We haven't received a file descriptor for this path yet. Assume that
// we will in a little while and save the request here.
mCachedFileDescriptorInfos.AppendElement(
new CachedFileDescriptorInfo(aPath, aCallback));
return false;
}
nsAutoPtr<CachedFileDescriptorInfo>& info =
mCachedFileDescriptorInfos[index];
MOZ_ASSERT(info);
MOZ_ASSERT(info->mPath == aPath);
MOZ_ASSERT(!info->mCallback);
MOZ_ASSERT(!info->mCanceled);
info->mCallback = aCallback;
nsRefPtr<CachedFileDescriptorCallbackRunnable> runnable =
new CachedFileDescriptorCallbackRunnable(info.forget());
runnable->Dispatch();
mCachedFileDescriptorInfos.RemoveElementAt(index);
return true;
}
void
TabChild::CancelCachedFileDescriptorCallback(
const nsAString& aPath,
nsICachedFileDescriptorListener* aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aPath.IsEmpty());
MOZ_ASSERT(aCallback);
const CachedFileDescriptorInfo search(aPath, aCallback);
uint32_t index =
mCachedFileDescriptorInfos.IndexOf(search, 0,
search.PathAndCallbackComparator());
if (index == mCachedFileDescriptorInfos.NoIndex) {
// Nothing to do here.
return;
}
nsAutoPtr<CachedFileDescriptorInfo>& info =
mCachedFileDescriptorInfos[index];
MOZ_ASSERT(info);
MOZ_ASSERT(info->mPath == aPath);
MOZ_ASSERT(!info->mFileDescriptor.IsValid());
MOZ_ASSERT(info->mCallback == aCallback);
MOZ_ASSERT(!info->mCanceled);
// Set this flag so that we will close the file descriptor when it arrives.
info->mCanceled = true;
}
void
TabChild::DoFakeShow()
{

View File

@ -54,7 +54,6 @@
#include "mozilla/dom/TabContext.h"
struct gfxMatrix;
class nsICachedFileDescriptorListener;
namespace mozilla {
namespace layout {
@ -197,9 +196,6 @@ public:
const mozilla::dom::StructuredCloneData& aData);
virtual bool RecvLoadURL(const nsCString& uri);
virtual bool RecvCacheFileDescriptor(const nsString& aPath,
const FileDescriptor& aFileDescriptor)
MOZ_OVERRIDE;
virtual bool RecvShow(const nsIntSize& size);
virtual bool RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size, const ScreenOrientation& orientation);
virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics);
@ -321,15 +317,6 @@ public:
*/
void GetAppType(nsAString& aAppType) const { aAppType = mAppType; }
// Returns true if the file descriptor was found in the cache, false
// otherwise.
bool GetCachedFileDescriptor(const nsAString& aPath,
nsICachedFileDescriptorListener* aCallback);
void CancelCachedFileDescriptorCallback(
const nsAString& aPath,
nsICachedFileDescriptorListener* aCallback);
protected:
virtual PRenderFrameChild* AllocPRenderFrame(ScrollingBehavior* aScrolling,
LayersBackend* aBackend,
@ -425,9 +412,6 @@ private:
return utils;
}
class CachedFileDescriptorInfo;
class CachedFileDescriptorCallbackRunnable;
nsCOMPtr<nsIWebNavigation> mWebNav;
nsCOMPtr<nsIWidget> mWidget;
nsCOMPtr<nsIURI> mLastURI;
@ -446,9 +430,6 @@ private:
// the touch we're tracking. That is, if touchend or a touchmove
// that exceeds the gesture threshold doesn't happen.
CancelableTask* mTapHoldTimer;
// At present only 1 of these is really expected.
nsAutoTArray<nsAutoPtr<CachedFileDescriptorInfo>, 1>
mCachedFileDescriptorInfos;
float mOldViewportWidth;
nscolor mLastBackgroundColor;
ScrollingBehavior mScrolling;

View File

@ -50,7 +50,6 @@
#include "nsSerializationHelper.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "private/pprio.h"
#include "StructuredCloneUtils.h"
#include "TabChild.h"
#include <algorithm>
@ -67,116 +66,6 @@ using namespace mozilla::dom::indexedDB;
// from the ones registered by webProgressListeners.
#define NOTIFY_FLAG_SHIFT 16
class OpenFileAndSendFDRunnable : public nsRunnable
{
const nsString mPath;
nsRefPtr<TabParent> mTabParent;
nsCOMPtr<nsIEventTarget> mEventTarget;
PRFileDesc* mFD;
public:
OpenFileAndSendFDRunnable(const nsAString& aPath, TabParent* aTabParent)
: mPath(aPath), mTabParent(aTabParent), mFD(nullptr)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aPath.IsEmpty());
MOZ_ASSERT(aTabParent);
}
void Dispatch()
{
MOZ_ASSERT(NS_IsMainThread());
mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
NS_ENSURE_TRUE_VOID(mEventTarget);
nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS_VOID(rv);
}
private:
~OpenFileAndSendFDRunnable()
{
MOZ_ASSERT(!mFD);
}
// This shouldn't be called directly except by the event loop. Use Dispatch
// to start the sequence.
NS_IMETHOD Run()
{
if (NS_IsMainThread()) {
SendResponse();
} else if (mFD) {
CloseFile();
} else {
OpenFile();
}
return NS_OK;
}
void SendResponse()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mTabParent);
MOZ_ASSERT(mEventTarget);
MOZ_ASSERT(mFD);
nsRefPtr<TabParent> tabParent;
mTabParent.swap(tabParent);
FileDescriptor::PlatformHandleType handle =
FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFD));
mozilla::unused << tabParent->SendCacheFileDescriptor(mPath, handle);
nsCOMPtr<nsIEventTarget> eventTarget;
mEventTarget.swap(eventTarget);
if (NS_FAILED(eventTarget->Dispatch(this, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch to stream transport service!");
// It's probably safer to take the main thread IO hit here rather
// than leak a file descriptor.
CloseFile();
}
}
void OpenFile()
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mFD);
nsCOMPtr<nsIFile> file;
nsresult rv = NS_NewLocalFile(mPath, false, getter_AddRefs(file));
NS_ENSURE_SUCCESS_VOID(rv);
PRFileDesc* fd;
rv = file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
NS_ENSURE_SUCCESS_VOID(rv);
mFD = fd;
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch to main thread!");
CloseFile();
}
}
void CloseFile()
{
// It's possible for this to happen on the main thread if the dispatch
// to the stream service fails after we've already opened the file so
// we can't assert the thread we're running on.
MOZ_ASSERT(mFD);
PR_Close(mFD);
mFD = nullptr;
}
};
namespace mozilla {
namespace dom {
@ -204,7 +93,6 @@ TabParent::TabParent(const TabContext& aContext)
, mUpdatedDimensions(false)
, mMarkedDestroying(false)
, mIsDestroyed(false)
, mAppPackageFileDescriptorSent(false)
{
}
@ -342,66 +230,23 @@ TabParent::AnswerCreateWindow(PBrowserParent** retval)
void
TabParent::LoadURL(nsIURI* aURI)
{
MOZ_ASSERT(aURI);
if (mIsDestroyed) {
return;
return;
}
if (!mShown) {
nsAutoCString spec;
if (aURI) {
aURI->GetSpec(spec);
}
NS_WARNING(nsPrintfCString("TabParent::LoadURL(%s) called before "
"Show(). Ignoring LoadURL.\n", spec.get()).get());
return;
}
nsCString spec;
aURI->GetSpec(spec);
if (!mShown) {
NS_WARNING(nsPrintfCString("TabParent::LoadURL(%s) called before "
"Show(). Ignoring LoadURL.\n",
spec.get()).get());
return;
}
unused << SendLoadURL(spec);
// If this app is a packaged app then we can speed startup by sending over
// the file descriptor for the "application.zip" file that it will
// invariably request. Only do this once.
if (!mAppPackageFileDescriptorSent) {
mAppPackageFileDescriptorSent = true;
nsCOMPtr<mozIApplication> app = GetOwnOrContainingApp();
if (app) {
nsString manifestURL;
nsresult rv = app->GetManifestURL(manifestURL);
NS_ENSURE_SUCCESS_VOID(rv);
if (StringBeginsWith(manifestURL, NS_LITERAL_STRING("app:"))) {
nsString basePath;
rv = app->GetBasePath(basePath);
NS_ENSURE_SUCCESS_VOID(rv);
nsString appId;
rv = app->GetId(appId);
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsIFile> packageFile;
rv = NS_NewLocalFile(basePath, false,
getter_AddRefs(packageFile));
NS_ENSURE_SUCCESS_VOID(rv);
rv = packageFile->Append(appId);
NS_ENSURE_SUCCESS_VOID(rv);
rv = packageFile->Append(NS_LITERAL_STRING("application.zip"));
NS_ENSURE_SUCCESS_VOID(rv);
nsString path;
rv = packageFile->GetPath(path);
NS_ENSURE_SUCCESS_VOID(rv);
nsRefPtr<OpenFileAndSendFDRunnable> openFileRunnable =
new OpenFileAndSendFDRunnable(path, this);
openFileRunnable->Dispatch();
}
}
}
}
void

View File

@ -304,8 +304,6 @@ private:
// When true, the TabParent is invalid and we should not send IPC messages
// anymore.
bool mIsDestroyed;
// Whether we have already sent a FileDescriptor for the app package.
bool mAppPackageFileDescriptorSent;
};
} // namespace dom

View File

@ -1,38 +0,0 @@
/* 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/. */
#ifndef mozilla_dom_ipc_nsICachedFileDescriptorListener_h
#define mozilla_dom_ipc_nsICachedFileDescriptorListener_h
#include "nsISupports.h"
#ifndef NS_NO_VTABLE
#define NS_NO_VTABLE
#endif
#define NS_ICACHEDFILEDESCRIPTORLISTENER_IID \
{0x2cedaee0, 0x6ef2, 0x4f60, {0x9a, 0x6c, 0xdf, 0x4e, 0x4d, 0x65, 0x6a, 0xf7}}
class nsAString;
namespace mozilla {
namespace ipc {
class FileDescriptor;
}
}
class NS_NO_VTABLE nsICachedFileDescriptorListener : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICACHEDFILEDESCRIPTORLISTENER_IID)
virtual void
OnCachedFileDescriptor(const nsAString& aPath,
const mozilla::ipc::FileDescriptor& aFD) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsICachedFileDescriptorListener,
NS_ICACHEDFILEDESCRIPTORLISTENER_IID)
#endif // mozilla_dom_ipc_nsICachedFileDescriptorListener_h

View File

@ -15,6 +15,16 @@ MOCHITEST_FILES = \
test_getUserMedia_basicAudio.html \
test_getUserMedia_basicVideo.html \
test_getUserMedia_basicVideoAudio.html \
test_getUserMedia_gumWithinGum.html \
test_getUserMedia_playAudioTwice.html \
test_getUserMedia_playVideoTwice.html \
test_getUserMedia_playVideoAudioTwice.html \
test_getUserMedia_stopAudioStream.html \
test_getUserMedia_stopAudioStreamWithFollowupAudio.html \
test_getUserMedia_stopVideoStreamWithFollowupVideo.html \
test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html \
test_getUserMedia_stopVideoStream.html \
test_getUserMedia_stopVideoAudioStream.html \
test_peerConnection_basicAudio.html \
test_peerConnection_basicAudioVideo.html \
test_peerConnection_basicAudioVideoCombined.html \

View File

@ -6,6 +6,9 @@ var Cc = SpecialPowers.Cc;
var Ci = SpecialPowers.Ci;
var Cr = SpecialPowers.Cr;
// Specifies whether we are using fake streams to run this automation
var FAKE_ENABLED = true;
/**
* Setup any Mochitest for WebRTC by enabling the preference for
* peer connections. As by bug 797979 it will also enable mozGetUserMedia().
@ -24,7 +27,9 @@ function runTest(aCallback, desktopSupportedOnly) {
ok(true, navigator.userAgent + ' currently not supported');
SimpleTest.finish();
} else {
SpecialPowers.pushPrefEnv({'set': [['media.peerconnection.enabled', true]]}, function () {
SpecialPowers.pushPrefEnv({'set': [
['media.peerconnection.enabled', true],
['media.navigator.permission.denied', true]]}, function () {
try {
aCallback();
}
@ -35,6 +40,22 @@ function runTest(aCallback, desktopSupportedOnly) {
}
}
/**
* Wrapper function for mozGetUserMedia to allow a singular area of control
* for determining whether we run this with fake devices or not.
*
* @param {Dictionary} constraints the constraints for this mozGetUserMedia
* callback
* @param {Function} onSuccess the success callback if the stream is
* successfully retrieved
* @param {Function} onError the error callback if the stream fails to be
* retrieved
*/
function getUserMedia(constraints, onSuccess, onError) {
constraints["fake"] = FAKE_ENABLED;
navigator.mozGetUserMedia(constraints, onSuccess, onError);
}
/**
* A callback function fired only under unexpected circumstances while
* running the tests. Kills off the test as well gracefully.

View File

@ -2,42 +2,63 @@
* 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/. */
var TIMEOUT_LENGTH = 10000;
/**
* This class manages playback of a HTMLMediaElement with a MediaStream.
* When constructed by a caller, an object instance is created with
* a media element and a media stream object.
*
* @param {HTMLMediaElement} mediaElement the media element for playback
* @param {LocalMediaStream} mediaStream the media stream used in
* the mediaElement for playback
* @param {MediaStream} mediaStream the media stream used in
* the mediaElement for playback
*/
function MediaStreamPlayback(mediaElement, mediaStream) {
/** The HTMLMediaElement used for media playback */
this.mediaElement = mediaElement;
/** The LocalMediaStream used with the HTMLMediaElement */
this.mediaStream = mediaStream;
}
MediaStreamPlayback.prototype = {
/**
* Starts media with a media stream, runs it until a canplaythrough and
* timeupdate event fires, and stops the media.
*
* @param {Boolean} isResume specifies if this media element is being resumed
* from a previous run
* @param {Function} onSuccess the success callback if the media playback
* start and stop cycle completes successfully
* @param {Function} onError the error callback if the media playback
* start and stop cycle fails
*/
playMedia : function MSP_playMedia(isResume, onSuccess, onError) {
var self = this;
this.startMedia(isResume, function() {
self.stopMediaElement();
onSuccess();
}, onError);
},
/**
* Starts the media with the associated stream.
*
* @param {Integer} timeoutLength the timeout length to wait for
* canplaythrough to fire in milliseconds
* @param {Boolean} isResume specifies if the media element playback
* is being resumed from a previous run
* @param {Function} onSuccess the success function call back
* if media starts correctly
* @param {Function} onError the error function call back
* if media fails to start
*/
this.startMedia = function(timeoutLength, onSuccess, onError) {
startMedia : function MSP_startMedia(isResume, onSuccess, onError) {
var self = this;
var canPlayThroughFired = false;
// Verifies we've received a correctly initialized LocalMediaStream
ok(this.mediaStream instanceof LocalMediaStream,
"Stream should be a LocalMediaStream");
is(this.mediaStream.currentTime, 0,
"Before starting the media element, currentTime = 0");
// If we're initially running this media, check that the time is zero
if (!isResume) {
is(this.mediaStream.currentTime, 0,
"Before starting the media element, currentTime = 0");
}
/**
* Callback fired when the canplaythrough event is fired. We only
@ -81,11 +102,11 @@ function MediaStreamPlayback(mediaElement, mediaStream) {
var timeUpdateFired = false;
var timeUpdateCallback = function() {
if(self.mediaStream.currentTime > 0 &&
self.mediaElement.currentTime > 0) {
if (self.mediaStream.currentTime > 0 &&
self.mediaElement.currentTime > 0) {
timeUpdateFired = true;
self.mediaElement.removeEventListener('timeupdate', timeUpdateCallback,
false);
self.mediaElement.removeEventListener('timeupdate',
timeUpdateCallback, false);
onSuccess();
}
};
@ -97,13 +118,13 @@ function MediaStreamPlayback(mediaElement, mediaStream) {
// If timeupdate doesn't fire in enough time, we fail the test
setTimeout(function() {
if(!timeUpdateFired) {
if (!timeUpdateFired) {
self.mediaElement.removeEventListener('timeupdate',
timeUpdateCallback, false);
ok(false, "timeUpdate event never fired");
onError();
}
}, timeoutLength);
}, TIMEOUT_LENGTH);
};
// Adds a listener intended to be fired when playback is available
@ -112,19 +133,19 @@ function MediaStreamPlayback(mediaElement, mediaStream) {
false);
// Hooks up the media stream to the media element and starts playing it
this.mediaElement.mozSrcObject = mediaStream;
this.mediaElement.mozSrcObject = this.mediaStream;
this.mediaElement.play();
// If canplaythrough doesn't fire in enough time, we fail the test
setTimeout(function() {
if(!canPlayThroughFired) {
if (!canPlayThroughFired) {
self.mediaElement.removeEventListener('canplaythrough',
canPlayThroughCallback, false);
ok(false, "canplaythrough event never fired");
onError();
}
}, timeoutLength);
};
}, TIMEOUT_LENGTH);
},
/**
* Stops the media with the associated stream.
@ -132,28 +153,93 @@ function MediaStreamPlayback(mediaElement, mediaStream) {
* Precondition: The media stream and element should both be actively
* being played.
*/
this.stopMedia = function() {
stopMediaElement : function MSP_stopMediaElement() {
this.mediaElement.pause();
this.mediaElement.mozSrcObject = null;
};
}
}
/**
* Starts media with a media stream, runs it until a canplaythrough and
* timeupdate event fires, and stops the media.
*
* @param {Integer} timeoutLength the length of time to wait for certain
* media events to fire
* @param {Function} onSuccess the success callback if the media playback
* start and stop cycle completes successfully
* @param {Function} onError the error callback if the media playback
* start and stop cycle fails
*/
this.playMedia = function(timeoutLength, onSuccess, onError) {
var self = this;
this.startMedia(timeoutLength, function() {
self.stopMedia();
/**
* This class is basically the same as MediaStreamPlayback except
* ensures that the instance provided startMedia is a MediaStream.
*
* @param {HTMLMediaElement} mediaElement the media element for playback
* @param {LocalMediaStream} mediaStream the media stream used in
* the mediaElement for playback
*/
function LocalMediaStreamPlayback(mediaElement, mediaStream) {
ok(mediaStream instanceof LocalMediaStream,
"Stream should be a LocalMediaStream");
MediaStreamPlayback.call(this, mediaElement, mediaStream);
}
// Sets up the inheritance chain from LMSP --> MSP
LocalMediaStreamPlayback.prototype = new MediaStreamPlayback();
LocalMediaStreamPlayback.prototype.constructor = LocalMediaStreamPlayback;
/**
* Starts media with a media stream, runs it until a canplaythrough and
* timeupdate event fires, and calls stop() on the stream.
*
* @param {Boolean} isResume specifies if this media element is being resumed
* from a previous run
* @param {Function} onSuccess the success callback if the media element
* successfully fires ended on a stop() call
* on the stream
* @param {Function} onError the error callback if the media element fails
* to fire an ended callback on a stop() call
* on the stream
*/
LocalMediaStreamPlayback.prototype.playMediaWithStreamStop = function(
isResume, onSuccess, onError) {
var self = this;
this.startMedia(isResume, function() {
self.stopStreamInMediaPlayback(function() {
self.stopMediaElement();
onSuccess();
}, onError);
};
}, onError);
}
/**
* Stops the local media stream while it's currently in playback in
* a media element.
*
* Precondition: The media stream and element should both be actively
* being played.
*
* @param {Function} onSuccess the success callback if the media element
* fires an ended event from stop() being called
* @param {Function} onError the error callback if the media element
* fails to fire an ended event from stop() being
* called
*/
LocalMediaStreamPlayback.prototype.stopStreamInMediaPlayback = function(
onSuccess, onError) {
var endedFired = false;
var self = this;
/**
* Callback fired when the ended event fires when stop() is called on the
* stream.
*/
var endedCallback = function() {
endedFired = true;
self.mediaElement.removeEventListener('ended', endedCallback, false);
ok(true, "ended event successfully fired");
onSuccess();
};
this.mediaElement.addEventListener('ended', endedCallback, false);
this.mediaStream.stop();
// If ended doesn't fire in enough time, then we fail the test
setTimeout(function() {
if (!endedFired) {
ok(false, "ended event never fired");
onError();
}
}, TIMEOUT_LENGTH);
}

View File

@ -27,13 +27,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=781534
runTest(function () {
var testAudio = document.getElementById('testAudio');
navigator.mozGetUserMedia({audio: true, fake: true}, function (aStream) {
var playback = new MediaStreamPlayback(testAudio, aStream);
playback.playMedia(10000, function () {
getUserMedia({audio: true}, function (aStream) {
var playback = new LocalMediaStreamPlayback(testAudio, aStream);
playback.playMedia(false, function () {
aStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>

View File

@ -27,13 +27,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=781534
runTest(function () {
var testVideo = document.getElementById('testVideo');
navigator.mozGetUserMedia({video: true, fake: true}, function (aStream) {
var playback = new MediaStreamPlayback(testVideo, aStream);
playback.playMedia(10000, function () {
getUserMedia({video: true}, function (aStream) {
var playback = new LocalMediaStreamPlayback(testVideo, aStream);
playback.playMedia(false, function () {
aStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>

View File

@ -27,13 +27,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=781534
runTest(function () {
var testVideoAudio = document.getElementById('testVideoAudio');
navigator.mozGetUserMedia({video: true, audio: true, fake: true},
function (aStream) {
var playback = new MediaStreamPlayback(testVideoAudio, aStream);
playback.playMedia(10000, function () {
aStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
getUserMedia({video: true, audio: true}, function (aStream) {
var playback = new LocalMediaStreamPlayback(testVideoAudio, aStream);
playback.playMedia(false, function () {
aStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);

View File

@ -1,4 +1,4 @@
<!DOCTYPE HTML>
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=795367

View File

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia gum within gum</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia gum within gum</a>
<p id="display"></p>
<div id="content" style="display: none">
<video id="testVideo"></video>
<audio id="testAudio"></audio>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test that we can complete a playback cycle for a video,
* then upon completion, do a playback cycle with audio, such that
* the audio gum call happens within the video gum call.
*/
runTest(function () {
getUserMedia({video: true}, function(videoStream) {
var testVideo = document.getElementById('testVideo');
var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
videoStream);
videoStreamPlayback.playMedia(false, function() {
getUserMedia({audio: true}, function(audioStream) {
var testAudio = document.getElementById('testAudio');
var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
audioStream);
audioStreamPlayback.playMedia(false, function() {
audioStream.stop();
videoStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Play Audio Twice</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Audio Twice</a>
<p id="display"></p>
<div id="content" style="display: none">
<audio id="testAudio"></audio>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test that we can complete an audio playback cycle twice in a row.
*/
runTest(function () {
getUserMedia({audio: true}, function(audioStream) {
var testAudio = document.getElementById('testAudio');
var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
audioStream);
audioStreamPlayback.playMedia(false, function() {
audioStreamPlayback.playMedia(true, function() {
audioStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Play Video and Audio Twice</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video and Audio Twice</a>
<p id="display"></p>
<div id="content" style="display: none">
<video id="testVideo"></video>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test that we can complete a video playback cycle twice in a row.
*/
runTest(function () {
getUserMedia({video: true, audio: true}, function(stream) {
var testVideo = document.getElementById('testVideo');
var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
streamPlayback.playMedia(false, function() {
streamPlayback.playMedia(true, function() {
stream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Play Video Twice</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video Twice</a>
<p id="display"></p>
<div id="content" style="display: none">
<video id="testVideo"></video>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test that we can complete a video playback cycle twice in a row.
*/
runTest(function () {
getUserMedia({video: true}, function(videoStream) {
var testVideo = document.getElementById('testVideo');
var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
videoStream);
videoStreamPlayback.playMedia(false, function() {
videoStreamPlayback.playMedia(true, function() {
videoStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Stop Audio Stream</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream</a>
<p id="display"></p>
<div id="content" style="display: none">
<audio id="testAudio"></video>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test to verify that we can start an audio stream in a media element,
* call stop() on the stream, and successfully get an ended event fired.
*/
runTest(function () {
getUserMedia({audio: true}, function(stream) {
var testAudio = document.getElementById('testAudio');
var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, stream);
audioStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Stop Audio Stream With Followup Audio</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream With Followup Audio</a>
<p id="display"></p>
<div id="content" style="display: none">
<audio id="testAudio"></audio>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test to verify that I can complete an audio gum playback in a media
* element, stop the stream, and then complete another audio gum playback
* in a media element.
*/
runTest(function () {
getUserMedia({audio: true}, function(firstStream) {
var testAudio = document.getElementById('testAudio');
var streamPlayback = new LocalMediaStreamPlayback(testAudio, firstStream);
streamPlayback.playMediaWithStreamStop(false, function() {
getUserMedia({audio: true}, function(secondStream) {
streamPlayback.mediaStream = secondStream;
streamPlayback.playMedia(false, function() {
secondStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Stop Video Audio Stream</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Audio Stream</a>
<p id="display"></p>
<div id="content" style="display: none">
<video id="testVideo"></video>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test to verify that we can start a video+audio stream in a
* media element, call stop() on the stream, and successfully get an
* ended event fired.
*/
runTest(function () {
getUserMedia({video: true, audio: true}, function(stream) {
var testVideo = document.getElementById('testVideo');
var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
streamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</a>
<p id="display"></p>
<div id="content" style="display: none">
<video id="testVideo"></video>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test to verify that I can complete an video+audio gum playback in a
* media element, stop the stream, and then complete another video+audio gum
* playback in a media element.
*/
runTest(function () {
getUserMedia({video: true, audio: true}, function(firstStream) {
var testVideo = document.getElementById('testVideo');
var streamPlayback = new LocalMediaStreamPlayback(testVideo, firstStream);
streamPlayback.playMediaWithStreamStop(false, function() {
getUserMedia({video: true, audio: true}, function(secondStream) {
streamPlayback.mediaStream = secondStream;
streamPlayback.playMedia(false, function() {
secondStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Stop Video Stream</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream</a>
<p id="display"></p>
<div id="content" style="display: none">
<video id="testVideo"></video>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test to verify that we can start a video stream in a media element,
* call stop() on the stream, and successfully get an ended event fired.
*/
runTest(function () {
getUserMedia({video: true}, function(stream) {
var testVideo = document.getElementById('testVideo');
var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
videoStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,53 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=822109
-->
<head>
<meta charset="utf-8">
<title>mozGetUserMedia Stop Video Stream With Followup Video</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream With Followup Video</a>
<p id="display"></p>
<div id="content" style="display: none">
<video id="testVideo"></video>
</div>
<pre id="test">
<script type="application/javascript">
/**
* Run a test to verify that I can complete an audio gum playback in a media
* element, stop the stream, and then complete another audio gum playback
* in a media element.
*/
runTest(function () {
getUserMedia({video: true}, function(firstStream) {
var testVideo = document.getElementById('testVideo');
var streamPlayback = new LocalMediaStreamPlayback(testVideo,
firstStream);
streamPlayback.playMediaWithStreamStop(false, function() {
getUserMedia({video: true}, function(secondStream) {
streamPlayback.mediaStream = secondStream;
streamPlayback.playMedia(false, function() {
secondStream.stop();
SimpleTest.finish();
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, unexpectedCallbackAndFinish);
}, true);
</script>
</pre>
</body>
</html>

View File

@ -30,7 +30,6 @@
#include "nsIOutputStream.h"
#include "nsIURL.h"
#include "nsTArray.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsIProtocolProxyService2.h"
#include "nsIStreamConverterService.h"
@ -68,9 +67,8 @@
#include "nsIXULRuntime.h"
// for the dialog
#include "nsIStringBundle.h"
#include "nsIWindowWatcher.h"
#include "nsPIDOMWindow.h"
#include "nsIDOMWindow.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptGlobalObjectOwner.h"
@ -200,20 +198,12 @@ PRLogModuleInfo* nsPluginLogging::gNPPLog = nullptr;
PRLogModuleInfo* nsPluginLogging::gPluginLog = nullptr;
#endif
#define BRAND_PROPERTIES_URL "chrome://branding/locale/brand.properties"
#define PLUGIN_PROPERTIES_URL "chrome://global/locale/downloadProgress.properties"
// #defines for plugin cache and prefs
#define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
// Raise this from '10' to '50' to work around a bug in Apple's current Java
// plugins on OS X Lion and SnowLeopard. See bug 705931.
#define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
#ifdef CALL_SAFETY_ON
// By default we run OOPP, so we don't want to cover up crashes.
bool gSkipPluginSafeCalls = true;
#endif
nsIFile *nsPluginHost::sPluginTempDir;
nsPluginHost *nsPluginHost::sInst;
@ -2180,11 +2170,6 @@ nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChange
{
Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
#ifdef CALL_SAFETY_ON
// check preferences on whether or not we want to try safe calls to plugins
NS_INIT_PLUGIN_SAFE_CALLS;
#endif
NS_ENSURE_ARG_POINTER(aPluginsChanged);
*aPluginsChanged = false;
@ -3256,98 +3241,6 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
return NS_OK;
}
nsresult
nsPluginHost::HandleBadPlugin(PRLibrary* aLibrary, nsNPAPIPluginInstance *aInstance)
{
// the |aLibrary| parameter is not needed anymore, after we added |aInstance| which
// can also be used to look up the plugin name, but we cannot get rid of it because
// the |nsIPluginHost| interface is deprecated which in fact means 'frozen'
NS_ERROR("Plugin performed illegal operation");
NS_ENSURE_ARG_POINTER(aInstance);
if (mDontShowBadPluginMessage)
return NS_OK;
nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
nsCOMPtr<nsIPrompt> prompt;
GetPrompt(owner, getter_AddRefs(prompt));
if (!prompt)
return NS_OK;
nsCOMPtr<nsIStringBundleService> strings =
mozilla::services::GetStringBundleService();
if (!strings)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIStringBundle> bundle;
nsresult rv = strings->CreateBundle(BRAND_PROPERTIES_URL, getter_AddRefs(bundle));
if (NS_FAILED(rv))
return rv;
nsXPIDLString brandName;
rv = bundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
getter_Copies(brandName));
if (NS_FAILED(rv))
return rv;
rv = strings->CreateBundle(PLUGIN_PROPERTIES_URL, getter_AddRefs(bundle));
if (NS_FAILED(rv))
return rv;
nsXPIDLString title, message, checkboxMessage;
rv = bundle->GetStringFromName(NS_LITERAL_STRING("BadPluginTitle").get(),
getter_Copies(title));
if (NS_FAILED(rv))
return rv;
const PRUnichar *formatStrings[] = { brandName.get() };
if (NS_FAILED(rv = bundle->FormatStringFromName(NS_LITERAL_STRING("BadPluginMessage").get(),
formatStrings, 1, getter_Copies(message))))
return rv;
rv = bundle->GetStringFromName(NS_LITERAL_STRING("BadPluginCheckboxMessage").get(),
getter_Copies(checkboxMessage));
if (NS_FAILED(rv))
return rv;
nsNPAPIPlugin *plugin = aInstance->GetPlugin();
if (!plugin)
return NS_ERROR_FAILURE;
nsPluginTag *pluginTag = TagForPlugin(plugin);
// add plugin name to the message
nsCString pluginname;
if (pluginTag) {
if (!pluginTag->mName.IsEmpty()) {
pluginname = pluginTag->mName;
} else {
pluginname = pluginTag->mFileName;
}
} else {
pluginname.AppendLiteral("???");
}
NS_ConvertUTF8toUTF16 msg(pluginname);
msg.AppendLiteral("\n\n");
msg.Append(message);
int32_t buttonPressed;
bool checkboxState = false;
rv = prompt->ConfirmEx(title, msg.get(),
nsIPrompt::BUTTON_TITLE_OK * nsIPrompt::BUTTON_POS_0,
nullptr, nullptr, nullptr,
checkboxMessage, &checkboxState, &buttonPressed);
if (NS_SUCCEEDED(rv) && checkboxState)
mDontShowBadPluginMessage = true;
return rv;
}
nsresult
nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
char **outPostData, uint32_t *outPostDataLen)

View File

@ -118,7 +118,6 @@ public:
nsresult GetPluginName(nsNPAPIPluginInstance *aPluginInstance, const char** aPluginName);
nsresult StopPluginInstance(nsNPAPIPluginInstance* aInstance);
nsresult HandleBadPlugin(PRLibrary* aLibrary, nsNPAPIPluginInstance *aInstance);
nsresult GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance, nsIPluginTag **aPluginTag);
nsresult
@ -265,7 +264,6 @@ private:
nsRefPtr<nsInvalidPluginTag> mInvalidPlugins;
nsTArray<nsCString> mPlayPreviewMimeTypes;
bool mPluginsLoaded;
bool mDontShowBadPluginMessage;
// set by pref plugin.override_internal_types
bool mOverrideInternalTypes;

View File

@ -6,8 +6,6 @@
#ifndef nsPluginSafety_h_
#define nsPluginSafety_h_
#include "npapi.h"
#include "nsPluginHost.h"
#include <prinrval.h>
// On Android, we need to guard against plugin code leaking entries in the local
@ -20,75 +18,9 @@
#define MAIN_THREAD_JNI_REF_GUARD
#endif
#if defined(XP_WIN)
#define CALL_SAFETY_ON
#endif
PRIntervalTime NS_NotifyBeginPluginCall();
void NS_NotifyPluginCall(PRIntervalTime);
#ifdef CALL_SAFETY_ON
#include "mozilla/Preferences.h"
extern bool gSkipPluginSafeCalls;
#define NS_INIT_PLUGIN_SAFE_CALLS \
PR_BEGIN_MACRO \
gSkipPluginSafeCalls = \
::mozilla::Preferences::GetBool("plugin.dont_try_safe_calls", \
gSkipPluginSafeCalls); \
PR_END_MACRO
#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst) \
PR_BEGIN_MACRO \
MAIN_THREAD_JNI_REF_GUARD; \
PRIntervalTime startTime = NS_NotifyBeginPluginCall(); \
if(gSkipPluginSafeCalls) \
ret = fun; \
else \
{ \
MOZ_SEH_TRY \
{ \
ret = fun; \
} \
MOZ_SEH_EXCEPT(true) \
{ \
nsresult res; \
nsCOMPtr<nsIPluginHost> host(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &res));\
if(NS_SUCCEEDED(res) && (host != nullptr)) \
static_cast<nsPluginHost*>(host.get())->HandleBadPlugin(nullptr, pluginInst); \
ret = (NPError)NS_ERROR_FAILURE; \
} \
} \
NS_NotifyPluginCall(startTime); \
PR_END_MACRO
#define NS_TRY_SAFE_CALL_VOID(fun, pluginInst) \
PR_BEGIN_MACRO \
MAIN_THREAD_JNI_REF_GUARD; \
PRIntervalTime startTime = NS_NotifyBeginPluginCall(); \
if(gSkipPluginSafeCalls) \
fun; \
else \
{ \
MOZ_SEH_TRY \
{ \
fun; \
} \
MOZ_SEH_EXCEPT(true) \
{ \
nsresult res; \
nsCOMPtr<nsIPluginHost> host(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &res));\
if(NS_SUCCEEDED(res) && (host != nullptr))\
static_cast<nsPluginHost*>(host.get())->HandleBadPlugin(nullptr, pluginInst);\
} \
} \
NS_NotifyPluginCall(startTime); \
PR_END_MACRO
#else // vanilla calls
#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst) \
PR_BEGIN_MACRO \
MAIN_THREAD_JNI_REF_GUARD; \
@ -105,6 +37,4 @@ PR_BEGIN_MACRO \
NS_NotifyPluginCall(startTime); \
PR_END_MACRO
#endif // CALL_SAFETY_ON
#endif //nsPluginSafety_h_

View File

@ -533,7 +533,8 @@ PluginModuleParent::EvaluateHangUIState(const bool aReset)
bool
PluginModuleParent::GetPluginName(nsAString& aPluginName)
{
nsPluginHost* host = nsPluginHost::GetInst();
nsRefPtr<nsPluginHost> host =
dont_AddRef<nsPluginHost>(nsPluginHost::GetInst());
if (!host) {
return false;
}

View File

@ -0,0 +1,25 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* http://www.whatwg.org/specs/web-apps/current-work/#the-progress-element
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
*/
interface HTMLProgressElement : HTMLElement {
[SetterThrows]
attribute double value;
[SetterThrows]
attribute double max;
readonly attribute double position;
/**
* The labels attribute will be done with bug 567740.
*/
//readonly attribute NodeList labels;
};

View File

@ -90,6 +90,7 @@ webidl_files = \
HTMLParagraphElement.webidl \
HTMLParamElement.webidl \
HTMLPreElement.webidl \
HTMLProgressElement.webidl \
HTMLPropertiesCollection.webidl \
HTMLQuoteElement.webidl \
HTMLScriptElement.webidl \

View File

@ -323,11 +323,16 @@ class DataSourceSurface : public SourceSurface
{
public:
virtual SurfaceType GetType() const { return SURFACE_DATA; }
/* Get the raw bitmap data of the surface */
/*
* Get the raw bitmap data of the surface.
* Can return null if there was OOM allocating surface data.
*/
virtual uint8_t *GetData() = 0;
/*
* Stride of the surface, distance in bytes between the start of the image
* data belonging to row y and row y+1. This may be negative.
* Can return 0 if there was OOM allocating surface data.
*/
virtual int32_t Stride() = 0;

View File

@ -521,6 +521,9 @@ LayerManagerOGL::Initialize(nsRefPtr<GLContext> aContext, bool force)
// initialise a common shader to check that we can actually compile a shader
if (!mPrograms[gl::RGBALayerProgramType].mVariations[MaskNone]->Initialize()) {
#ifdef MOZ_WIDGET_ANDROID
NS_RUNTIMEABORT("Shader initialization failed");
#endif
return false;
}
@ -591,6 +594,9 @@ LayerManagerOGL::Initialize(nsRefPtr<GLContext> aContext, bool force)
if (mFBOTextureTarget == LOCAL_GL_NONE) {
/* Unable to find a texture target that works with FBOs and NPOT textures */
#ifdef MOZ_WIDGET_ANDROID
NS_RUNTIMEABORT("No texture target");
#endif
return false;
}
} else {
@ -608,6 +614,9 @@ LayerManagerOGL::Initialize(nsRefPtr<GLContext> aContext, bool force)
* texture2DRect).
*/
if (!mGLContext->IsExtensionSupported(gl::GLContext::ARB_texture_rectangle))
#ifdef MOZ_WIDGET_ANDROID
NS_RUNTIMEABORT("No texture rectangle");
#endif
return false;
}

View File

@ -1209,7 +1209,11 @@ qcms_transform* qcms_transform_create(
precache = true;
}
if (qcms_supports_iccv4 && (in->A2B0 || out->B2A0 || in->mAB || out->mAB)) {
// This precache assumes RGB_SIGNATURE (fails on GRAY_SIGNATURE, for instance)
if (qcms_supports_iccv4 &&
(in_type == QCMS_DATA_RGB_8 || in_type == QCMS_DATA_RGBA_8) &&
(in->A2B0 || out->B2A0 || in->mAB || out->mAB))
{
// Precache the transformation to a CLUT 33x33x33 in size.
// 33 is used by many profiles and works well in pratice.
// This evenly divides 256 into blocks of 8x8x8.

View File

@ -33,7 +33,7 @@ using namespace mozilla::services;
#define PROXY_IF_SANDBOXED(_call) \
do { \
if (InSandbox()) { \
if (!hal_sandbox::IsHalChildLive()) { \
if (!hal_sandbox::HalChildDestroyed()) { \
hal_sandbox::_call; \
} \
} else { \
@ -44,7 +44,7 @@ using namespace mozilla::services;
#define RETURN_PROXY_IF_SANDBOXED(_call, defValue)\
do { \
if (InSandbox()) { \
if (hal_sandbox::IsHalChildLive()) { \
if (hal_sandbox::HalChildDestroyed()) { \
return defValue; \
} \
return hal_sandbox::_call; \

View File

@ -94,7 +94,11 @@ void EnableSystemTimezoneChangeNotifications();
*/
void DisableSystemTimezoneChangeNotifications();
bool IsHalChildLive();
/**
* Has the child-side HAL IPC object been destroyed? If so, you shouldn't send
* messages to hal_sandbox.
*/
bool HalChildDestroyed();
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla

View File

@ -25,12 +25,12 @@ using namespace mozilla::hal;
namespace mozilla {
namespace hal_sandbox {
static bool sHalChildIsLive = false;
static bool sHalChildDestroyed = false;
bool
IsHalChildLive()
HalChildDestroyed()
{
return sHalChildIsLive;
return sHalChildDestroyed;
}
static PHalChild* sHal;
@ -810,7 +810,7 @@ public:
virtual void
ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
{
sHalChildIsLive = true;
sHalChildDestroyed = true;
}
virtual bool

View File

@ -1,64 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/clipboard.h"
#include "base/logging.h"
namespace {
// A compromised renderer could send us bad data, so validate it.
bool IsBitmapSafe(const Clipboard::ObjectMapParams& params) {
const gfx::Size* size =
reinterpret_cast<const gfx::Size*>(&(params[1].front()));
return params[0].size() ==
static_cast<size_t>(size->width() * size->height() * 4);
}
}
void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
switch (type) {
case CBF_TEXT:
WriteText(&(params[0].front()), params[0].size());
break;
case CBF_HTML:
if (params.size() == 2)
WriteHTML(&(params[0].front()), params[0].size(),
&(params[1].front()), params[1].size());
else
WriteHTML(&(params[0].front()), params[0].size(), NULL, 0);
break;
case CBF_BOOKMARK:
WriteBookmark(&(params[0].front()), params[0].size(),
&(params[1].front()), params[1].size());
break;
case CBF_LINK:
WriteHyperlink(&(params[0].front()), params[0].size(),
&(params[1].front()), params[1].size());
break;
case CBF_FILES:
WriteFiles(&(params[0].front()), params[0].size());
break;
case CBF_WEBKIT:
WriteWebSmartPaste();
break;
#if defined(OS_WIN) || defined(OS_LINUX)
case CBF_BITMAP:
if (!IsBitmapSafe(params))
return;
WriteBitmap(&(params[0].front()), &(params[1].front()));
break;
#endif // defined(OS_WIN) || defined(OS_LINUX)
default:
NOTREACHED();
}
}

View File

@ -1,204 +0,0 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CLIPBOARD_H_
#define BASE_CLIPBOARD_H_
#include <map>
#include <string>
#include <vector>
#include "base/file_path.h"
#include "base/process.h"
#include "base/string16.h"
#include "base/gfx/size.h"
class Clipboard {
public:
typedef std::string FormatType;
#if defined(OS_LINUX)
typedef struct _GtkClipboard GtkClipboard;
typedef std::map<FormatType, std::pair<char*, size_t> > TargetMap;
#endif
// ObjectType designates the type of data to be stored in the clipboard. This
// designation is shared across all OSes. The system-specific designation
// is defined by FormatType. A single ObjectType might be represented by
// several system-specific FormatTypes. For example, on Linux the CBF_TEXT
// ObjectType maps to "text/plain", "STRING", and several other formats. On
// windows it maps to CF_UNICODETEXT.
enum ObjectType {
CBF_TEXT,
CBF_HTML,
CBF_BOOKMARK,
CBF_LINK,
CBF_FILES,
CBF_WEBKIT,
CBF_BITMAP,
CBF_SMBITMAP // bitmap from shared memory
};
// ObjectMap is a map from ObjectType to associated data.
// The data is organized differently for each ObjectType. The following
// table summarizes what kind of data is stored for each key.
// * indicates an optional argument.
//
// Key Arguments Type
// -------------------------------------
// CBF_TEXT text char array
// CBF_HTML html char array
// url* char array
// CBF_BOOKMARK html char array
// url char array
// CBF_LINK html char array
// url char array
// CBF_FILES files char array representing multiple files.
// Filenames are separated by null characters and
// the final filename is double null terminated.
// The filenames are encoded in platform-specific
// encoding.
// CBF_WEBKIT none empty vector
// CBF_BITMAP pixels byte array
// size gfx::Size struct
// CBF_SMBITMAP shared_mem shared memory handle
// size gfx::Size struct
typedef std::vector<char> ObjectMapParam;
typedef std::vector<ObjectMapParam> ObjectMapParams;
typedef std::map<int /* ObjectType */, ObjectMapParams> ObjectMap;
Clipboard();
~Clipboard();
// Write a bunch of objects to the system clipboard. Copies are made of the
// contents of |objects|. On Windows they are copied to the system clipboard.
// On linux they are copied into a structure owned by the Clipboard object and
// kept until the system clipboard is set again.
void WriteObjects(const ObjectMap& objects);
// Behaves as above. If there is some shared memory handle passed as one of
// the objects, it came from the process designated by |process|. This will
// assist in turning it into a shared memory region that the current process
// can use.
void WriteObjects(const ObjectMap& objects, base::ProcessHandle process);
// Tests whether the clipboard contains a certain format
bool IsFormatAvailable(const FormatType& format) const;
// Reads UNICODE text from the clipboard, if available.
void ReadText(string16* result) const;
// Reads ASCII text from the clipboard, if available.
void ReadAsciiText(std::string* result) const;
// Reads HTML from the clipboard, if available.
void ReadHTML(string16* markup, std::string* src_url) const;
// Reads a bookmark from the clipboard, if available.
void ReadBookmark(string16* title, std::string* url) const;
// Reads a file or group of files from the clipboard, if available, into the
// out parameter.
void ReadFile(FilePath* file) const;
void ReadFiles(std::vector<FilePath>* files) const;
// Get format Identifiers for various types.
static FormatType GetUrlFormatType();
static FormatType GetUrlWFormatType();
static FormatType GetMozUrlFormatType();
static FormatType GetPlainTextFormatType();
static FormatType GetPlainTextWFormatType();
static FormatType GetFilenameFormatType();
static FormatType GetFilenameWFormatType();
static FormatType GetWebKitSmartPasteFormatType();
// Win: MS HTML Format, Other: Generic HTML format
static FormatType GetHtmlFormatType();
#if defined(OS_WIN)
static FormatType GetBitmapFormatType();
// Firefox text/html
static FormatType GetTextHtmlFormatType();
static FormatType GetCFHDropFormatType();
static FormatType GetFileDescriptorFormatType();
static FormatType GetFileContentFormatZeroType();
// Duplicates any remote shared memory handle embedded inside |objects| that
// was created by |process| so that it can be used by this process.
static void DuplicateRemoteHandles(base::ProcessHandle process,
ObjectMap* objects);
#endif
private:
void WriteText(const char* text_data, size_t text_len);
void WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len);
void WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len);
void WriteHyperlink(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len);
void WriteWebSmartPaste();
void WriteFiles(const char* file_data, size_t file_len);
void DispatchObject(ObjectType type, const ObjectMapParams& params);
void WriteBitmap(const char* pixel_data, const char* size_data);
#if defined(OS_WIN)
void WriteBitmapFromSharedMemory(const char* bitmap_data,
const char* size_data,
base::ProcessHandle handle);
void WriteBitmapFromHandle(HBITMAP source_hbitmap,
const gfx::Size& size);
// Safely write to system clipboard. Free |handle| on failure.
void WriteToClipboard(unsigned int format, HANDLE handle);
static void ParseBookmarkClipboardFormat(const string16& bookmark,
string16* title,
std::string* url);
// Free a handle depending on its type (as intuited from format)
static void FreeData(unsigned int format, HANDLE data);
// Return the window that should be the clipboard owner, creating it
// if neccessary. Marked const for lazily initialization by const methods.
HWND GetClipboardWindow() const;
// Mark this as mutable so const methods can still do lazy initialization.
mutable HWND clipboard_owner_;
// True if we can create a window.
bool create_window_;
#elif defined(OS_LINUX)
// Data is stored in the |clipboard_data_| map until it is saved to the system
// clipboard. The Store* functions save data to the |clipboard_data_| map. The
// SetGtkClipboard function replaces whatever is on the system clipboard with
// the contents of |clipboard_data_|.
// The Write* functions make a deep copy of the data passed to them an store
// it in |clipboard_data_|.
// Write changes to gtk clipboard.
void SetGtkClipboard();
// Free pointers in clipboard_data_ and clear() the map.
void FreeTargetMap();
// Insert a mapping into clipboard_data_.
void InsertMapping(const char* key, char* data, size_t data_len);
TargetMap* clipboard_data_;
GtkClipboard* clipboard_;
#endif
DISALLOW_EVIL_CONSTRUCTORS(Clipboard);
};
#endif // BASE_CLIPBOARD_H_

View File

@ -1,328 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/clipboard.h"
#include <gtk/gtk.h>
#include <map>
#include <set>
#include <string>
#include <utility>
#include "base/scoped_ptr.h"
#include "base/linux_util.h"
#include "base/string_util.h"
namespace {
const char kMimeBmp[] = "image/bmp";
const char kMimeHtml[] = "text/html";
const char kMimeText[] = "text/plain";
const char kMimeWebkitSmartPaste[] = "chromium-internal/webkit-paste";
std::string GdkAtomToString(const GdkAtom& atom) {
gchar* name = gdk_atom_name(atom);
std::string rv(name);
g_free(name);
return rv;
}
GdkAtom StringToGdkAtom(const std::string& str) {
return gdk_atom_intern(str.c_str(), false);
}
// GtkClipboardGetFunc callback.
// GTK will call this when an application wants data we copied to the clipboard.
void GetData(GtkClipboard* clipboard,
GtkSelectionData* selection_data,
guint info,
gpointer user_data) {
Clipboard::TargetMap* data_map =
reinterpret_cast<Clipboard::TargetMap*>(user_data);
std::string target_string = GdkAtomToString(selection_data->target);
Clipboard::TargetMap::iterator iter = data_map->find(target_string);
if (iter == data_map->end())
return;
if (target_string == kMimeBmp) {
gtk_selection_data_set_pixbuf(selection_data,
reinterpret_cast<GdkPixbuf*>(iter->second.first));
} else {
gtk_selection_data_set(selection_data, selection_data->target, 8,
reinterpret_cast<guchar*>(iter->second.first),
iter->second.second);
}
}
// GtkClipboardClearFunc callback.
// We are guaranteed this will be called exactly once for each call to
// gtk_clipboard_set_with_data
void ClearData(GtkClipboard* clipboard,
gpointer user_data) {
Clipboard::TargetMap* map =
reinterpret_cast<Clipboard::TargetMap*>(user_data);
std::set<char*> ptrs;
for (Clipboard::TargetMap::iterator iter = map->begin();
iter != map->end(); ++iter) {
if (iter->first == kMimeBmp)
g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
else
ptrs.insert(iter->second.first);
}
for (std::set<char*>::iterator iter = ptrs.begin();
iter != ptrs.end(); ++iter)
delete[] *iter;
delete map;
}
// Frees the pointers in the given map and clears the map.
// Does not double-free any pointers.
void FreeTargetMap(Clipboard::TargetMap map) {
std::set<char*> ptrs;
for (Clipboard::TargetMap::iterator iter = map.begin();
iter != map.end(); ++iter)
ptrs.insert(iter->second.first);
for (std::set<char*>::iterator iter = ptrs.begin();
iter != ptrs.end(); ++iter)
delete[] *iter;
map.clear();
}
// Called on GdkPixbuf destruction; see WriteBitmap().
void GdkPixbufFree(guchar* pixels, gpointer data) {
free(pixels);
}
} // namespace
Clipboard::Clipboard() {
clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
}
Clipboard::~Clipboard() {
// TODO(estade): do we want to save clipboard data after we exit?
// gtk_clipboard_set_can_store and gtk_clipboard_store work
// but have strangely awful performance.
}
void Clipboard::WriteObjects(const ObjectMap& objects) {
clipboard_data_ = new TargetMap();
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
SetGtkClipboard();
}
// Take ownership of the GTK clipboard and inform it of the targets we support.
void Clipboard::SetGtkClipboard() {
scoped_array<GtkTargetEntry> targets(
new GtkTargetEntry[clipboard_data_->size()]);
int i = 0;
for (Clipboard::TargetMap::iterator iter = clipboard_data_->begin();
iter != clipboard_data_->end(); ++iter, ++i) {
char* target_string = new char[iter->first.size() + 1];
strcpy(target_string, iter->first.c_str());
targets[i].target = target_string;
targets[i].flags = 0;
targets[i].info = i;
}
gtk_clipboard_set_with_data(clipboard_, targets.get(),
clipboard_data_->size(),
GetData, ClearData,
clipboard_data_);
for (size_t i = 0; i < clipboard_data_->size(); i++)
delete[] targets[i].target;
}
void Clipboard::WriteText(const char* text_data, size_t text_len) {
char* data = new char[text_len];
memcpy(data, text_data, text_len);
InsertMapping(kMimeText, data, text_len);
InsertMapping("TEXT", data, text_len);
InsertMapping("STRING", data, text_len);
InsertMapping("UTF8_STRING", data, text_len);
InsertMapping("COMPOUND_TEXT", data, text_len);
}
void Clipboard::WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
// TODO(estade): might not want to ignore |url_data|
char* data = new char[markup_len];
memcpy(data, markup_data, markup_len);
InsertMapping(kMimeHtml, data, markup_len);
}
// Write an extra flavor that signifies WebKit was the last to modify the
// pasteboard. This flavor has no data.
void Clipboard::WriteWebSmartPaste() {
InsertMapping(kMimeWebkitSmartPaste, NULL, 0);
}
void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
guchar* data = base::BGRAToRGBA(reinterpret_cast<const uint8_t*>(pixel_data),
size->width(), size->height(), 0);
GdkPixbuf* pixbuf =
gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE,
8, size->width(), size->height(),
size->width() * 4, GdkPixbufFree, NULL);
// We store the GdkPixbuf*, and the size_t half of the pair is meaningless.
// Note that this contrasts with the vast majority of entries in our target
// map, which directly store the data and its length.
InsertMapping(kMimeBmp, reinterpret_cast<char*>(pixbuf), 0);
}
void Clipboard::WriteBookmark(const char* title_data, size_t title_len,
const char* url_data, size_t url_len) {
// TODO(estade): implement this, but for now fail silently so we do not
// write error output during layout tests.
// NOTIMPLEMENTED();
}
void Clipboard::WriteHyperlink(const char* title_data, size_t title_len,
const char* url_data, size_t url_len) {
NOTIMPLEMENTED();
}
void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
NOTIMPLEMENTED();
}
// We do not use gtk_clipboard_wait_is_target_available because of
// a bug with the gtk clipboard. It caches the available targets
// and does not always refresh the cache when it is appropriate.
// TODO(estade): When gnome bug 557315 is resolved, change this function
// to use gtk_clipboard_wait_is_target_available. Also, catch requests
// for plain text and change them to gtk_clipboard_wait_is_text_available
// (which checks for several standard text targets).
bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const {
bool retval = false;
GdkAtom* targets = NULL;
GtkSelectionData* data =
gtk_clipboard_wait_for_contents(clipboard_,
gdk_atom_intern("TARGETS", false));
if (!data)
return false;
int num = 0;
gtk_selection_data_get_targets(data, &targets, &num);
GdkAtom format_atom = StringToGdkAtom(format);
for (int i = 0; i < num; i++) {
if (targets[i] == format_atom) {
retval = true;
break;
}
}
gtk_selection_data_free(data);
g_free(targets);
return retval;
}
void Clipboard::ReadText(string16* result) const {
result->clear();
gchar* text = gtk_clipboard_wait_for_text(clipboard_);
if (text == NULL)
return;
// TODO(estade): do we want to handle the possible error here?
UTF8ToUTF16(text, strlen(text), result);
g_free(text);
}
void Clipboard::ReadAsciiText(std::string* result) const {
result->clear();
gchar* text = gtk_clipboard_wait_for_text(clipboard_);
if (text == NULL)
return;
result->assign(text);
g_free(text);
}
void Clipboard::ReadFile(FilePath* file) const {
*file = FilePath();
}
// TODO(estade): handle different charsets.
void Clipboard::ReadHTML(string16* markup, std::string* src_url) const {
markup->clear();
GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard_,
StringToGdkAtom(GetHtmlFormatType()));
if (!data)
return;
UTF8ToUTF16(reinterpret_cast<char*>(data->data),
strlen(reinterpret_cast<char*>(data->data)),
markup);
gtk_selection_data_free(data);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
return GdkAtomToString(GDK_TARGET_STRING);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
return GetPlainTextFormatType();
}
// static
Clipboard::FormatType Clipboard::GetHtmlFormatType() {
return std::string(kMimeHtml);
}
// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
return std::string(kMimeWebkitSmartPaste);
}
// Insert the key/value pair in the clipboard_data structure. If
// the mapping already exists, it frees the associated data. Don't worry
// about double freeing because if the same key is inserted into the
// map twice, it must have come from different Write* functions and the
// data pointer cannot be the same.
void Clipboard::InsertMapping(const char* key,
char* data,
size_t data_len) {
TargetMap::iterator iter = clipboard_data_->find(key);
if (iter != clipboard_data_->end()) {
if (strcmp(kMimeBmp, key) == 0)
g_object_unref(reinterpret_cast<GdkPixbuf*>(iter->second.first));
else
delete[] iter->second.first;
}
(*clipboard_data_)[key] = std::make_pair(data, data_len);
}

View File

@ -1,283 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/clipboard.h"
#import <Cocoa/Cocoa.h>
#include "base/logging.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
namespace {
// Would be nice if this were in UTCoreTypes.h, but it isn't
const NSString* kUTTypeURLName = @"public.url-name";
// Tells us if WebKit was the last to write to the pasteboard. There's no
// actual data associated with this type.
const NSString *kWebSmartPastePboardType = @"NeXT smart paste pasteboard type";
NSPasteboard* GetPasteboard() {
// The pasteboard should not be nil in a UI session, but this handy DCHECK
// can help track down problems if someone tries using clipboard code outside
// of a UI session.
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
DCHECK(pasteboard);
return pasteboard;
}
} // namespace
Clipboard::Clipboard() {
}
Clipboard::~Clipboard() {
}
void Clipboard::WriteObjects(const ObjectMap& objects) {
NSPasteboard* pb = GetPasteboard();
[pb declareTypes:[NSArray array] owner:nil];
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
}
void Clipboard::WriteText(const char* text_data, size_t text_len) {
std::string text_str(text_data, text_len);
NSString *text = base::SysUTF8ToNSString(text_str);
NSPasteboard* pb = GetPasteboard();
[pb addTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
[pb setString:text forType:NSStringPboardType];
}
void Clipboard::WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
std::string html_fragment_str(markup_data, markup_len);
NSString *html_fragment = base::SysUTF8ToNSString(html_fragment_str);
// TODO(avi): url_data?
NSPasteboard* pb = GetPasteboard();
[pb addTypes:[NSArray arrayWithObject:NSHTMLPboardType] owner:nil];
[pb setString:html_fragment forType:NSHTMLPboardType];
}
void Clipboard::WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
WriteHyperlink(title_data, title_len, url_data, url_len);
}
void Clipboard::WriteHyperlink(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
std::string title_str(title_data, title_len);
NSString *title = base::SysUTF8ToNSString(title_str);
std::string url_str(url_data, url_len);
NSString *url = base::SysUTF8ToNSString(url_str);
// TODO(playmobil): In the Windows version of this function, an HTML
// representation of the bookmark is also added to the clipboard, to support
// drag and drop of web shortcuts. I don't think we need to do this on the
// Mac, but we should double check later on.
NSURL* nsurl = [NSURL URLWithString:url];
NSPasteboard* pb = GetPasteboard();
// passing UTIs into the pasteboard methods is valid >= 10.5
[pb addTypes:[NSArray arrayWithObjects:NSURLPboardType,
kUTTypeURLName,
nil]
owner:nil];
[nsurl writeToPasteboard:pb];
[pb setString:title forType:kUTTypeURLName];
}
void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
NSMutableArray* fileList = [NSMutableArray arrayWithCapacity:1];
// Offset of current filename from start of file_data array.
size_t current_filename_offset = 0;
// file_data is double null terminated (see table at top of clipboard.h).
// So this loop can ignore the second null terminator, thus file_len - 1.
// TODO(playmobil): If we need a loop like this on other platforms then split
// this out into a common function that outputs a std::vector<const char*>.
for (size_t i = 0; i < file_len - 1; ++i) {
if (file_data[i] == '\0') {
const char* filename = &file_data[current_filename_offset];
[fileList addObject:[NSString stringWithUTF8String:filename]];
current_filename_offset = i + 1;
continue;
}
}
NSPasteboard* pb = GetPasteboard();
[pb addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
[pb setPropertyList:fileList forType:NSFilenamesPboardType];
}
// Write an extra flavor that signifies WebKit was the last to modify the
// pasteboard. This flavor has no data.
void Clipboard::WriteWebSmartPaste() {
NSPasteboard* pb = GetPasteboard();
NSString* format = base::SysUTF8ToNSString(GetWebKitSmartPasteFormatType());
[pb addTypes:[NSArray arrayWithObject:format] owner:nil];
[pb setData:nil forType:format];
}
bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const {
NSString* format_ns = base::SysUTF8ToNSString(format);
NSPasteboard* pb = GetPasteboard();
NSArray* types = [pb types];
return [types containsObject:format_ns];
}
void Clipboard::ReadText(string16* result) const {
NSPasteboard* pb = GetPasteboard();
NSString* contents = [pb stringForType:NSStringPboardType];
UTF8ToUTF16([contents UTF8String],
[contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
result);
}
void Clipboard::ReadAsciiText(std::string* result) const {
NSPasteboard* pb = GetPasteboard();
NSString* contents = [pb stringForType:NSStringPboardType];
if (!contents)
result->clear();
else
result->assign([contents UTF8String]);
}
void Clipboard::ReadHTML(string16* markup, std::string* src_url) const {
if (markup) {
NSPasteboard* pb = GetPasteboard();
NSArray *supportedTypes = [NSArray arrayWithObjects:NSHTMLPboardType,
NSStringPboardType,
nil];
NSString *bestType = [pb availableTypeFromArray:supportedTypes];
NSString* contents = [pb stringForType:bestType];
UTF8ToUTF16([contents UTF8String],
[contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
markup);
}
// TODO(avi): src_url?
if (src_url)
src_url->clear();
}
void Clipboard::ReadBookmark(string16* title, std::string* url) const {
NSPasteboard* pb = GetPasteboard();
if (title) {
NSString* contents = [pb stringForType:kUTTypeURLName];
UTF8ToUTF16([contents UTF8String],
[contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
title);
}
if (url) {
NSString* url_string = [[NSURL URLFromPasteboard:pb] absoluteString];
if (!url_string)
url->clear();
else
url->assign([url_string UTF8String]);
}
}
void Clipboard::ReadFile(FilePath* file) const {
if (!file) {
NOTREACHED();
return;
}
*file = FilePath();
std::vector<FilePath> files;
ReadFiles(&files);
// Take the first file, if available.
if (!files.empty())
*file = files[0];
}
void Clipboard::ReadFiles(std::vector<FilePath>* files) const {
if (!files) {
NOTREACHED();
return;
}
files->clear();
NSPasteboard* pb = GetPasteboard();
NSArray* fileList = [pb propertyListForType:NSFilenamesPboardType];
for (unsigned int i = 0; i < [fileList count]; ++i) {
std::string file = [[fileList objectAtIndex:i] UTF8String];
files->push_back(FilePath(file));
}
}
// static
Clipboard::FormatType Clipboard::GetUrlFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSURLPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetUrlWFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSURLPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSStringPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSStringPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetFilenameFormatType() {
static const std::string type =
base::SysNSStringToUTF8(NSFilenamesPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
static const std::string type =
base::SysNSStringToUTF8(NSFilenamesPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetHtmlFormatType() {
static const std::string type = base::SysNSStringToUTF8(NSHTMLPboardType);
return type;
}
// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
static const std::string type =
base::SysNSStringToUTF8(kWebSmartPastePboardType);
return type;
}

View File

@ -1,282 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/basictypes.h"
#include "base/clipboard.h"
#include "base/message_loop.h"
#include "base/scoped_clipboard_writer.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#if defined(OS_WIN)
class ClipboardTest : public PlatformTest {
protected:
virtual void SetUp() {
message_loop_.reset(new MessageLoopForUI());
}
virtual void TearDown() {
}
private:
scoped_ptr<MessageLoop> message_loop_;
};
#elif defined(OS_POSIX)
typedef PlatformTest ClipboardTest;
#endif // defined(OS_WIN)
TEST_F(ClipboardTest, ClearTest) {
Clipboard clipboard;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteText(ASCIIToUTF16("clear me"));
}
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(ASCIIToUTF16("<b>broom</b>"), "");
}
EXPECT_FALSE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextWFormatType()));
EXPECT_FALSE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextFormatType()));
}
TEST_F(ClipboardTest, TextTest) {
Clipboard clipboard;
string16 text(ASCIIToUTF16("This is a string16!#$")), text_result;
std::string ascii_text;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteText(text);
}
EXPECT_TRUE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextWFormatType()));
EXPECT_TRUE(clipboard.IsFormatAvailable(
Clipboard::GetPlainTextFormatType()));
clipboard.ReadText(&text_result);
EXPECT_EQ(text, text_result);
clipboard.ReadAsciiText(&ascii_text);
EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}
TEST_F(ClipboardTest, HTMLTest) {
Clipboard clipboard;
string16 markup(ASCIIToUTF16("<string>Hi!</string>")), markup_result;
std::string url("http://www.example.com/"), url_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(markup, url);
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetHtmlFormatType()));
clipboard.ReadHTML(&markup_result, &url_result);
EXPECT_EQ(markup, markup_result);
#if defined(OS_WIN)
// TODO(playmobil): It's not clear that non windows clipboards need to support
// this.
EXPECT_EQ(url, url_result);
#endif // defined(OS_WIN)
}
TEST_F(ClipboardTest, TrickyHTMLTest) {
Clipboard clipboard;
string16 markup(ASCIIToUTF16("<em>Bye!<!--EndFragment --></em>")),
markup_result;
std::string url, url_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(markup, url);
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetHtmlFormatType()));
clipboard.ReadHTML(&markup_result, &url_result);
EXPECT_EQ(markup, markup_result);
#if defined(OS_WIN)
// TODO(playmobil): It's not clear that non windows clipboards need to support
// this.
EXPECT_EQ(url, url_result);
#endif // defined(OS_WIN)
}
// TODO(estade): Port the following test (decide what target we use for urls)
#if !defined(OS_LINUX)
TEST_F(ClipboardTest, BookmarkTest) {
Clipboard clipboard;
string16 title(ASCIIToUTF16("The Example Company")), title_result;
std::string url("http://www.example.com/"), url_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteBookmark(title, url);
}
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetUrlWFormatType()));
clipboard.ReadBookmark(&title_result, &url_result);
EXPECT_EQ(title, title_result);
EXPECT_EQ(url, url_result);
}
#endif // defined(OS_WIN)
TEST_F(ClipboardTest, MultiFormatTest) {
Clipboard clipboard;
string16 text(ASCIIToUTF16("Hi!")), text_result;
string16 markup(ASCIIToUTF16("<strong>Hi!</string>")), markup_result;
std::string url("http://www.example.com/"), url_result;
std::string ascii_text;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHTML(markup, url);
clipboard_writer.WriteText(text);
}
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType()));
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetPlainTextWFormatType()));
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetPlainTextFormatType()));
clipboard.ReadHTML(&markup_result, &url_result);
EXPECT_EQ(markup, markup_result);
#if defined(OS_WIN)
// TODO(playmobil): It's not clear that non windows clipboards need to support
// this.
EXPECT_EQ(url, url_result);
#endif // defined(OS_WIN)
clipboard.ReadText(&text_result);
EXPECT_EQ(text, text_result);
clipboard.ReadAsciiText(&ascii_text);
EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}
// TODO(estade): Port the following tests (decide what targets we use for files)
#if !defined(OS_LINUX)
// Files for this test don't actually need to exist on the file system, just
// don't try to use a non-existent file you've retrieved from the clipboard.
TEST_F(ClipboardTest, FileTest) {
Clipboard clipboard;
#if defined(OS_WIN)
FilePath file(L"C:\\Downloads\\My Downloads\\A Special File.txt");
#elif defined(OS_MACOSX)
// OS X will print a warning message if we stick a non-existant file on the
// clipboard.
FilePath file("/usr/bin/make");
#endif // defined(OS_MACOSX)
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteFile(file);
}
FilePath out_file;
clipboard.ReadFile(&out_file);
EXPECT_EQ(file.value(), out_file.value());
}
TEST_F(ClipboardTest, MultipleFilesTest) {
Clipboard clipboard;
#if defined(OS_WIN)
FilePath file1(L"C:\\Downloads\\My Downloads\\File 1.exe");
FilePath file2(L"C:\\Downloads\\My Downloads\\File 2.pdf");
FilePath file3(L"C:\\Downloads\\My Downloads\\File 3.doc");
#elif defined(OS_MACOSX)
// OS X will print a warning message if we stick a non-existant file on the
// clipboard.
FilePath file1("/usr/bin/make");
FilePath file2("/usr/bin/man");
FilePath file3("/usr/bin/perl");
#endif // defined(OS_MACOSX)
std::vector<FilePath> files;
files.push_back(file1);
files.push_back(file2);
files.push_back(file3);
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteFiles(files);
}
std::vector<FilePath> out_files;
clipboard.ReadFiles(&out_files);
EXPECT_EQ(files.size(), out_files.size());
for (size_t i = 0; i < out_files.size(); ++i)
EXPECT_EQ(files[i].value(), out_files[i].value());
}
#endif // !defined(OS_LINUX)
#if defined(OS_WIN) // Windows only tests.
TEST_F(ClipboardTest, HyperlinkTest) {
Clipboard clipboard;
string16 title(ASCIIToUTF16("The Example Company")), title_result;
std::string url("http://www.example.com/"), url_result;
string16 html(ASCIIToUTF16("<a href=\"http://www.example.com/\">"
"The Example Company</a>")), html_result;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteHyperlink(title, url);
}
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetUrlWFormatType()));
EXPECT_EQ(true,
clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType()));
clipboard.ReadBookmark(&title_result, &url_result);
EXPECT_EQ(title, title_result);
EXPECT_EQ(url, url_result);
clipboard.ReadHTML(&html_result, &url_result);
EXPECT_EQ(html, html_result);
}
TEST_F(ClipboardTest, WebSmartPasteTest) {
Clipboard clipboard;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteWebSmartPaste();
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetWebKitSmartPasteFormatType()));
}
TEST_F(ClipboardTest, BitmapTest) {
unsigned int fake_bitmap[] = {
0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
};
Clipboard clipboard;
{
ScopedClipboardWriter clipboard_writer(&clipboard);
clipboard_writer.WriteBitmapFromPixels(fake_bitmap, gfx::Size(3, 4));
}
EXPECT_EQ(true, clipboard.IsFormatAvailable(
Clipboard::GetBitmapFormatType()));
}
#endif // defined(OS_WIN)

View File

@ -1,488 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/clipboard_util.h"
#include <shellapi.h>
#include <shlwapi.h>
#include <wininet.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/scoped_handle.h"
#include "base/string_util.h"
namespace {
bool GetUrlFromHDrop(IDataObject* data_object, std::wstring* url,
std::wstring* title) {
DCHECK(data_object && url && title);
STGMEDIUM medium;
if (FAILED(data_object->GetData(ClipboardUtil::GetCFHDropFormat(), &medium)))
return false;
HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
if (!hdrop)
return false;
bool success = false;
wchar_t filename[MAX_PATH];
if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) {
wchar_t url_buffer[INTERNET_MAX_URL_LENGTH];
if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") &&
GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer,
arraysize(url_buffer), filename)) {
*url = url_buffer;
PathRemoveExtension(filename);
title->assign(PathFindFileName(filename));
success = true;
}
}
DragFinish(hdrop);
GlobalUnlock(medium.hGlobal);
// We don't need to call ReleaseStgMedium here because as far as I can tell,
// DragFinish frees the hGlobal for us.
return success;
}
bool SplitUrlAndTitle(const std::wstring& str, std::wstring* url,
std::wstring* title) {
DCHECK(url && title);
size_t newline_pos = str.find('\n');
bool success = false;
if (newline_pos != std::string::npos) {
*url = str.substr(0, newline_pos);
title->assign(str.substr(newline_pos + 1));
success = true;
} else {
*url = str;
title->assign(str);
success = true;
}
return success;
}
} // namespace
FORMATETC* ClipboardUtil::GetUrlFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_INETURLA);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetUrlWFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_INETURLW);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetMozUrlFormat() {
// The format is "URL\nTitle"
static UINT cf = RegisterClipboardFormat(L"text/x-moz-url");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetPlainTextFormat() {
// We don't need to register this format since it's a built in format.
static FORMATETC format = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetPlainTextWFormat() {
// We don't need to register this format since it's a built in format.
static FORMATETC format = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1,
TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFilenameWFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEW);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFilenameFormat()
{
static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetHtmlFormat() {
static UINT cf = RegisterClipboardFormat(L"HTML Format");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetTextHtmlFormat() {
static UINT cf = RegisterClipboardFormat(L"text/html");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetCFHDropFormat() {
// We don't need to register this format since it's a built in format.
static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1,
TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFileDescriptorFormat() {
static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
return &format;
}
FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
return &format;
}
bool ClipboardUtil::HasUrl(IDataObject* data_object) {
DCHECK(data_object);
return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetFilenameFormat()));
}
bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
DCHECK(data_object);
return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat()));
}
bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
DCHECK(data_object);
return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) ||
SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat()));
}
bool ClipboardUtil::GetUrl(IDataObject* data_object,
std::wstring* url, std::wstring* title) {
DCHECK(data_object && url && title);
if (!HasUrl(data_object))
return false;
// Try to extract a URL from |data_object| in a variety of formats.
STGMEDIUM store;
if (GetUrlFromHDrop(data_object, url, title)) {
return true;
}
if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) ||
SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) {
// Mozilla URL format or unicode URL
ScopedHGlobal<wchar_t> data(store.hGlobal);
bool success = SplitUrlAndTitle(data.get(), url, title);
ReleaseStgMedium(&store);
if (success)
return true;
}
if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
// URL using ascii
ScopedHGlobal<char> data(store.hGlobal);
bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
ReleaseStgMedium(&store);
if (success)
return true;
}
if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) {
// filename using unicode
ScopedHGlobal<wchar_t> data(store.hGlobal);
bool success = false;
if (data.get() && data.get()[0] && (PathFileExists(data.get()) ||
PathIsUNC(data.get()))) {
wchar_t file_url[INTERNET_MAX_URL_LENGTH];
DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
0))) {
*url = file_url;
title->assign(file_url);
success = true;
}
}
ReleaseStgMedium(&store);
if (success)
return true;
}
if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) {
// filename using ascii
ScopedHGlobal<char> data(store.hGlobal);
bool success = false;
if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
PathIsUNCA(data.get()))) {
char file_url[INTERNET_MAX_URL_LENGTH];
DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
if (SUCCEEDED(::UrlCreateFromPathA(data.get(),
file_url,
&file_url_len,
0))) {
*url = UTF8ToWide(file_url);
title->assign(*url);
success = true;
}
}
ReleaseStgMedium(&store);
if (success)
return true;
}
return false;
}
bool ClipboardUtil::GetFilenames(IDataObject* data_object,
std::vector<std::wstring>* filenames) {
DCHECK(data_object && filenames);
if (!HasFilenames(data_object))
return false;
STGMEDIUM medium;
if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium)))
return false;
HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
if (!hdrop)
return false;
const int kMaxFilenameLen = 4096;
const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
for (unsigned int i = 0; i < num_files; ++i) {
wchar_t filename[kMaxFilenameLen];
if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
continue;
filenames->push_back(filename);
}
DragFinish(hdrop);
GlobalUnlock(medium.hGlobal);
// We don't need to call ReleaseStgMedium here because as far as I can tell,
// DragFinish frees the hGlobal for us.
return true;
}
bool ClipboardUtil::GetPlainText(IDataObject* data_object,
std::wstring* plain_text) {
DCHECK(data_object && plain_text);
if (!HasPlainText(data_object))
return false;
STGMEDIUM store;
bool success = false;
if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
// Unicode text
ScopedHGlobal<wchar_t> data(store.hGlobal);
plain_text->assign(data.get());
ReleaseStgMedium(&store);
success = true;
} else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
// ascii text
ScopedHGlobal<char> data(store.hGlobal);
plain_text->assign(UTF8ToWide(data.get()));
ReleaseStgMedium(&store);
success = true;
} else {
//If a file is dropped on the window, it does not provide either of the
//plain text formats, so here we try to forcibly get a url.
std::wstring title;
success = GetUrl(data_object, plain_text, &title);
}
return success;
}
bool ClipboardUtil::GetHtml(IDataObject* data_object,
std::wstring* html, std::string* base_url) {
DCHECK(data_object && html && base_url);
if (SUCCEEDED(data_object->QueryGetData(GetHtmlFormat()))) {
STGMEDIUM store;
if (SUCCEEDED(data_object->GetData(GetHtmlFormat(), &store))) {
// MS CF html
ScopedHGlobal<char> data(store.hGlobal);
std::string html_utf8;
CFHtmlToHtml(std::string(data.get(), data.Size()), &html_utf8, base_url);
html->assign(UTF8ToWide(html_utf8));
ReleaseStgMedium(&store);
return true;
}
}
if (FAILED(data_object->QueryGetData(GetTextHtmlFormat())))
return false;
STGMEDIUM store;
if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
return false;
// text/html
ScopedHGlobal<wchar_t> data(store.hGlobal);
html->assign(data.get());
ReleaseStgMedium(&store);
return true;
}
bool ClipboardUtil::GetFileContents(IDataObject* data_object,
std::wstring* filename, std::string* file_contents) {
DCHECK(data_object && filename && file_contents);
bool has_data =
SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) ||
SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat()));
if (!has_data)
return false;
STGMEDIUM content;
// The call to GetData can be very slow depending on what is in
// |data_object|.
if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) {
if (TYMED_HGLOBAL == content.tymed) {
ScopedHGlobal<char> data(content.hGlobal);
file_contents->assign(data.get(), data.Size());
}
ReleaseStgMedium(&content);
}
STGMEDIUM description;
if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
&description))) {
ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
// We expect there to be at least one file in here.
DCHECK(fgd->cItems >= 1);
filename->assign(fgd->fgd[0].cFileName);
ReleaseStgMedium(&description);
}
return true;
}
// HtmlToCFHtml and CFHtmlToHtml are based on similar methods in
// WebCore/platform/win/ClipboardUtilitiesWin.cpp.
/*
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Helper method for converting from text/html to MS CF_HTML.
// Documentation for the CF_HTML format is available at
// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx
std::string ClipboardUtil::HtmlToCFHtml(const std::string& html,
const std::string& base_url) {
if (html.empty())
return std::string();
#define MAX_DIGITS 10
#define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
#define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
#define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
static const char* header = "Version:0.9\r\n"
"StartHTML:" NUMBER_FORMAT "\r\n"
"EndHTML:" NUMBER_FORMAT "\r\n"
"StartFragment:" NUMBER_FORMAT "\r\n"
"EndFragment:" NUMBER_FORMAT "\r\n";
static const char* source_url_prefix = "SourceURL:";
static const char* start_markup =
"<html>\r\n<body>\r\n<!--StartFragment-->\r\n";
static const char* end_markup =
"\r\n<!--EndFragment-->\r\n</body>\r\n</html>";
// Calculate offsets
size_t start_html_offset = strlen(header) - strlen(NUMBER_FORMAT) * 4 +
MAX_DIGITS * 4;
if (!base_url.empty()) {
start_html_offset += strlen(source_url_prefix) +
base_url.length() + 2; // Add 2 for \r\n.
}
size_t start_fragment_offset = start_html_offset + strlen(start_markup);
size_t end_fragment_offset = start_fragment_offset + html.length();
size_t end_html_offset = end_fragment_offset + strlen(end_markup);
std::string result = StringPrintf(header, start_html_offset,
end_html_offset, start_fragment_offset, end_fragment_offset);
if (!base_url.empty()) {
result.append(source_url_prefix);
result.append(base_url);
result.append("\r\n");
}
result.append(start_markup);
result.append(html);
result.append(end_markup);
#undef MAX_DIGITS
#undef MAKE_NUMBER_FORMAT_1
#undef MAKE_NUMBER_FORMAT_2
#undef NUMBER_FORMAT
return result;
}
// Helper method for converting from MS CF_HTML to text/html.
void ClipboardUtil::CFHtmlToHtml(const std::string& cf_html,
std::string* html,
std::string* base_url) {
// Obtain base_url if present.
static std::string src_url_str("SourceURL:");
size_t line_start = cf_html.find(src_url_str);
if (line_start != std::string::npos) {
size_t src_end = cf_html.find("\n", line_start);
size_t src_start = line_start + src_url_str.length();
if (src_end != std::string::npos && src_start != std::string::npos) {
*base_url = cf_html.substr(src_start, src_end - src_start);
TrimWhitespace(*base_url, TRIM_ALL, base_url);
}
}
// Find the markup between "<!--StartFragment -->" and "<!--EndFragment-->".
std::string cf_html_lower = StringToLowerASCII(cf_html);
size_t markup_start = cf_html_lower.find("<html", 0);
size_t tag_start = cf_html.find("StartFragment", markup_start);
size_t fragment_start = cf_html.find('>', tag_start) + 1;
size_t tag_end = cf_html.rfind("EndFragment", std::string::npos);
size_t fragment_end = cf_html.rfind('<', tag_end);
if (fragment_start != std::string::npos &&
fragment_end != std::string::npos) {
*html = cf_html.substr(fragment_start, fragment_end - fragment_start);
TrimWhitespace(*html, TRIM_ALL, html);
}
}

View File

@ -1,63 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Some helper functions for working with the clipboard and IDataObjects.
#ifndef BASE_CLIPBOARD_UTIL_H_
#define BASE_CLIPBOARD_UTIL_H_
#include <shlobj.h>
#include <string>
#include <vector>
class ClipboardUtil {
public:
/////////////////////////////////////////////////////////////////////////////
// Clipboard formats.
static FORMATETC* GetUrlFormat();
static FORMATETC* GetUrlWFormat();
static FORMATETC* GetMozUrlFormat();
static FORMATETC* GetPlainTextFormat();
static FORMATETC* GetPlainTextWFormat();
static FORMATETC* GetFilenameFormat();
static FORMATETC* GetFilenameWFormat();
// MS HTML Format
static FORMATETC* GetHtmlFormat();
// Firefox text/html
static FORMATETC* GetTextHtmlFormat();
static FORMATETC* GetCFHDropFormat();
static FORMATETC* GetFileDescriptorFormat();
static FORMATETC* GetFileContentFormatZero();
static FORMATETC* GetWebKitSmartPasteFormat();
/////////////////////////////////////////////////////////////////////////////
// These methods check to see if |data_object| has the requested type.
// Returns true if it does.
static bool HasUrl(IDataObject* data_object);
static bool HasFilenames(IDataObject* data_object);
static bool HasPlainText(IDataObject* data_object);
/////////////////////////////////////////////////////////////////////////////
// Helper methods to extract information from an IDataObject. These methods
// return true if the requested data type is found in |data_object|.
static bool GetUrl(IDataObject* data_object,
std::wstring* url, std::wstring* title);
static bool GetFilenames(IDataObject* data_object,
std::vector<std::wstring>* filenames);
static bool GetPlainText(IDataObject* data_object, std::wstring* plain_text);
static bool GetHtml(IDataObject* data_object, std::wstring* text_html,
std::string* base_url);
static bool GetFileContents(IDataObject* data_object,
std::wstring* filename,
std::string* file_contents);
// A helper method for converting between MS CF_HTML format and plain
// text/html.
static std::string HtmlToCFHtml(const std::string& html,
const std::string& base_url);
static void CFHtmlToHtml(const std::string& cf_html, std::string* html,
std::string* base_url);
};
#endif // BASE_CLIPBOARD_UTIL_H_

View File

@ -1,650 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Many of these functions are based on those found in
// webkit/port/platform/PasteboardWin.cpp
#include "base/clipboard.h"
#include <shlobj.h>
#include <shellapi.h>
#include "base/clipboard_util.h"
#include "base/lock.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/shared_memory.h"
#include "base/string_util.h"
namespace {
// A scoper to manage acquiring and automatically releasing the clipboard.
class ScopedClipboard {
public:
ScopedClipboard() : opened_(false) { }
~ScopedClipboard() {
if (opened_)
Release();
}
bool Acquire(HWND owner) {
const int kMaxAttemptsToOpenClipboard = 5;
if (opened_) {
NOTREACHED();
return false;
}
// Attempt to open the clipboard, which will acquire the Windows clipboard
// lock. This may fail if another process currently holds this lock.
// We're willing to try a few times in the hopes of acquiring it.
//
// This turns out to be an issue when using remote desktop because the
// rdpclip.exe process likes to read what we've written to the clipboard and
// send it to the RDP client. If we open and close the clipboard in quick
// succession, we might be trying to open it while rdpclip.exe has it open,
// See Bug 815425.
//
// In fact, we believe we'll only spin this loop over remote desktop. In
// normal situations, the user is initiating clipboard operations and there
// shouldn't be contention.
for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
// If we didn't manage to open the clipboard, sleep a bit and be hopeful.
if (attempts != 0)
::Sleep(5);
if (::OpenClipboard(owner)) {
opened_ = true;
return true;
}
}
// We failed to acquire the clipboard.
return false;
}
void Release() {
if (opened_) {
::CloseClipboard();
opened_ = false;
} else {
NOTREACHED();
}
}
private:
bool opened_;
};
LRESULT CALLBACK ClipboardOwnerWndProc(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
LRESULT lresult = 0;
switch (message) {
case WM_RENDERFORMAT:
// This message comes when SetClipboardData was sent a null data handle
// and now it's come time to put the data on the clipboard.
// We always set data, so there isn't a need to actually do anything here.
break;
case WM_RENDERALLFORMATS:
// This message comes when SetClipboardData was sent a null data handle
// and now this application is about to quit, so it must put data on
// the clipboard before it exits.
// We always set data, so there isn't a need to actually do anything here.
break;
case WM_DRAWCLIPBOARD:
break;
case WM_DESTROY:
break;
case WM_CHANGECBCHAIN:
break;
default:
lresult = DefWindowProc(hwnd, message, wparam, lparam);
break;
}
return lresult;
}
template <typename charT>
HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
HGLOBAL data =
::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
if (data) {
charT* raw_data = static_cast<charT*>(::GlobalLock(data));
memcpy(raw_data, str.data(), str.size() * sizeof(charT));
raw_data[str.size()] = '\0';
::GlobalUnlock(data);
}
return data;
};
} // namespace
Clipboard::Clipboard() : create_window_(false) {
if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) {
// Make a dummy HWND to be the clipboard's owner.
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = ClipboardOwnerWndProc;
wcex.hInstance = GetModuleHandle(NULL);
wcex.lpszClassName = L"ClipboardOwnerWindowClass";
::RegisterClassEx(&wcex);
create_window_ = true;
}
clipboard_owner_ = NULL;
}
Clipboard::~Clipboard() {
if (clipboard_owner_)
::DestroyWindow(clipboard_owner_);
clipboard_owner_ = NULL;
}
void Clipboard::WriteObjects(const ObjectMap& objects) {
WriteObjects(objects, NULL);
}
void Clipboard::WriteObjects(const ObjectMap& objects,
base::ProcessHandle process) {
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
::EmptyClipboard();
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
if (iter->first == CBF_SMBITMAP)
WriteBitmapFromSharedMemory(&(iter->second[0].front()),
&(iter->second[1].front()),
process);
else
DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
}
void Clipboard::WriteText(const char* text_data, size_t text_len) {
string16 text;
UTF8ToUTF16(text_data, text_len, &text);
HGLOBAL glob = CreateGlobalData(text);
WriteToClipboard(CF_UNICODETEXT, glob);
}
void Clipboard::WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
std::string markup(markup_data, markup_len);
std::string url;
if (url_len > 0)
url.assign(url_data, url_len);
std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url);
HGLOBAL glob = CreateGlobalData(html_fragment);
WriteToClipboard(StringToInt(GetHtmlFormatType()), glob);
}
void Clipboard::WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
std::string bookmark(title_data, title_len);
bookmark.append(1, L'\n');
bookmark.append(url_data, url_len);
string16 wide_bookmark = UTF8ToWide(bookmark);
HGLOBAL glob = CreateGlobalData(wide_bookmark);
WriteToClipboard(StringToInt(GetUrlWFormatType()), glob);
}
void Clipboard::WriteHyperlink(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
// Store as a bookmark.
WriteBookmark(title_data, title_len, url_data, url_len);
std::string title(title_data, title_len),
url(url_data, url_len),
link("<a href=\"");
// Construct the hyperlink.
link.append(url);
link.append("\">");
link.append(title);
link.append("</a>");
// Store hyperlink as html.
WriteHTML(link.c_str(), link.size(), NULL, 0);
}
void Clipboard::WriteWebSmartPaste() {
DCHECK(clipboard_owner_);
::SetClipboardData(StringToInt(GetWebKitSmartPasteFormatType()), NULL);
}
void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
HDC dc = ::GetDC(NULL);
// This doesn't actually cost us a memcpy when the bitmap comes from the
// renderer as we load it into the bitmap using setPixels which just sets a
// pointer. Someone has to memcpy it into GDI, it might as well be us here.
// TODO(darin): share data in gfx/bitmap_header.cc somehow
BITMAPINFO bm_info = {0};
bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bm_info.bmiHeader.biWidth = size->width();
bm_info.bmiHeader.biHeight = -size->height(); // sets vertical orientation
bm_info.bmiHeader.biPlanes = 1;
bm_info.bmiHeader.biBitCount = 32;
bm_info.bmiHeader.biCompression = BI_RGB;
// ::CreateDIBSection allocates memory for us to copy our bitmap into.
// Unfortunately, we can't write the created bitmap to the clipboard,
// (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
void *bits;
HBITMAP source_hbitmap =
::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
if (bits && source_hbitmap) {
// Copy the bitmap out of shared memory and into GDI
memcpy(bits, pixel_data, 4 * size->width() * size->height());
// Now we have an HBITMAP, we can write it to the clipboard
WriteBitmapFromHandle(source_hbitmap, *size);
}
::DeleteObject(source_hbitmap);
::ReleaseDC(NULL, dc);
}
void Clipboard::WriteBitmapFromSharedMemory(const char* bitmap_data,
const char* size_data,
base::ProcessHandle process) {
const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
// bitmap_data has an encoded shared memory object. See
// DuplicateRemoteHandles().
char* ptr = const_cast<char*>(bitmap_data);
scoped_ptr<const base::SharedMemory> bitmap(*
reinterpret_cast<const base::SharedMemory**>(ptr));
// TODO(darin): share data in gfx/bitmap_header.cc somehow.
BITMAPINFO bm_info = {0};
bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bm_info.bmiHeader.biWidth = size->width();
// Sets the vertical orientation.
bm_info.bmiHeader.biHeight = -size->height();
bm_info.bmiHeader.biPlanes = 1;
bm_info.bmiHeader.biBitCount = 32;
bm_info.bmiHeader.biCompression = BI_RGB;
HDC dc = ::GetDC(NULL);
// We can create an HBITMAP directly using the shared memory handle, saving
// a memcpy.
HBITMAP source_hbitmap =
::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL,
bitmap->handle(), 0);
if (source_hbitmap) {
// Now we can write the HBITMAP to the clipboard
WriteBitmapFromHandle(source_hbitmap, *size);
}
::DeleteObject(source_hbitmap);
::ReleaseDC(NULL, dc);
}
void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
const gfx::Size& size) {
// We would like to just call ::SetClipboardData on the source_hbitmap,
// but that bitmap might not be of a sort we can write to the clipboard.
// For this reason, we create a new bitmap, copy the bits over, and then
// write that to the clipboard.
HDC dc = ::GetDC(NULL);
HDC compatible_dc = ::CreateCompatibleDC(NULL);
HDC source_dc = ::CreateCompatibleDC(NULL);
// This is the HBITMAP we will eventually write to the clipboard
HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
if (!hbitmap) {
// Failed to create the bitmap
::DeleteDC(compatible_dc);
::DeleteDC(source_dc);
::ReleaseDC(NULL, dc);
return;
}
HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
// Now we need to blend it into an HBITMAP we can place on the clipboard
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
::GdiAlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
source_dc, 0, 0, size.width(), size.height(), bf);
// Clean up all the handles we just opened
::SelectObject(compatible_dc, old_hbitmap);
::SelectObject(source_dc, old_source);
::DeleteObject(old_hbitmap);
::DeleteObject(old_source);
::DeleteDC(compatible_dc);
::DeleteDC(source_dc);
::ReleaseDC(NULL, dc);
WriteToClipboard(CF_BITMAP, hbitmap);
}
// Write a file or set of files to the clipboard in HDROP format. When the user
// invokes a paste command (in a Windows explorer shell, for example), the files
// will be copied to the paste location.
void Clipboard::WriteFiles(const char* file_data, size_t file_len) {
// Calculate the amount of space we'll need store the strings and
// a DROPFILES struct.
size_t bytes = sizeof(DROPFILES) + file_len;
HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
if (!hdata)
return;
char* data = static_cast<char*>(::GlobalLock(hdata));
DROPFILES* drop_files = reinterpret_cast<DROPFILES*>(data);
drop_files->pFiles = sizeof(DROPFILES);
drop_files->fWide = TRUE;
memcpy(data + sizeof(DROPFILES), file_data, file_len);
::GlobalUnlock(hdata);
WriteToClipboard(CF_HDROP, hdata);
}
void Clipboard::WriteToClipboard(unsigned int format, HANDLE handle) {
DCHECK(clipboard_owner_);
if (handle && !::SetClipboardData(format, handle)) {
DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError());
FreeData(format, handle);
}
}
bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const {
return ::IsClipboardFormatAvailable(StringToInt(format)) != FALSE;
}
void Clipboard::ReadText(string16* result) const {
if (!result) {
NOTREACHED();
return;
}
result->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
if (!data)
return;
result->assign(static_cast<const char16*>(::GlobalLock(data)));
::GlobalUnlock(data);
}
void Clipboard::ReadAsciiText(std::string* result) const {
if (!result) {
NOTREACHED();
return;
}
result->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(CF_TEXT);
if (!data)
return;
result->assign(static_cast<const char*>(::GlobalLock(data)));
::GlobalUnlock(data);
}
void Clipboard::ReadHTML(string16* markup, std::string* src_url) const {
if (markup)
markup->clear();
if (src_url)
src_url->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(StringToInt(GetHtmlFormatType()));
if (!data)
return;
std::string html_fragment(static_cast<const char*>(::GlobalLock(data)));
::GlobalUnlock(data);
std::string markup_utf8;
ClipboardUtil::CFHtmlToHtml(html_fragment, &markup_utf8, src_url);
markup->assign(UTF8ToWide(markup_utf8));
}
void Clipboard::ReadBookmark(string16* title, std::string* url) const {
if (title)
title->clear();
if (url)
url->clear();
// Acquire the clipboard.
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HANDLE data = ::GetClipboardData(StringToInt(GetUrlWFormatType()));
if (!data)
return;
string16 bookmark(static_cast<const char16*>(::GlobalLock(data)));
::GlobalUnlock(data);
ParseBookmarkClipboardFormat(bookmark, title, url);
}
// Read a file in HDROP format from the clipboard.
void Clipboard::ReadFile(FilePath* file) const {
if (!file) {
NOTREACHED();
return;
}
*file = FilePath();
std::vector<FilePath> files;
ReadFiles(&files);
// Take the first file, if available.
if (!files.empty())
*file = files[0];
}
// Read a set of files in HDROP format from the clipboard.
void Clipboard::ReadFiles(std::vector<FilePath>* files) const {
if (!files) {
NOTREACHED();
return;
}
files->clear();
ScopedClipboard clipboard;
if (!clipboard.Acquire(GetClipboardWindow()))
return;
HDROP drop = static_cast<HDROP>(::GetClipboardData(CF_HDROP));
if (!drop)
return;
// Count of files in the HDROP.
int count = ::DragQueryFile(drop, 0xffffffff, NULL, 0);
if (count) {
for (int i = 0; i < count; ++i) {
int size = ::DragQueryFile(drop, i, NULL, 0) + 1;
std::wstring file;
::DragQueryFile(drop, i, WriteInto(&file, size), size);
files->push_back(FilePath(file));
}
}
}
// static
void Clipboard::ParseBookmarkClipboardFormat(const string16& bookmark,
string16* title,
std::string* url) {
const string16 kDelim = ASCIIToUTF16("\r\n");
const size_t title_end = bookmark.find_first_of(kDelim);
if (title)
title->assign(bookmark.substr(0, title_end));
if (url) {
const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
if (url_start != string16::npos)
*url = UTF16ToUTF8(bookmark.substr(url_start, string16::npos));
}
}
// static
Clipboard::FormatType Clipboard::GetUrlFormatType() {
return IntToString(ClipboardUtil::GetUrlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetUrlWFormatType() {
return IntToString(ClipboardUtil::GetUrlWFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetMozUrlFormatType() {
return IntToString(ClipboardUtil::GetMozUrlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextFormatType() {
return IntToString(ClipboardUtil::GetPlainTextFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetPlainTextWFormatType() {
return IntToString(ClipboardUtil::GetPlainTextWFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFilenameFormatType() {
return IntToString(ClipboardUtil::GetFilenameFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFilenameWFormatType() {
return IntToString(ClipboardUtil::GetFilenameWFormat()->cfFormat);
}
// MS HTML Format
// static
Clipboard::FormatType Clipboard::GetHtmlFormatType() {
return IntToString(ClipboardUtil::GetHtmlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetBitmapFormatType() {
return IntToString(CF_BITMAP);
}
// Firefox text/html
// static
Clipboard::FormatType Clipboard::GetTextHtmlFormatType() {
return IntToString(ClipboardUtil::GetTextHtmlFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetCFHDropFormatType() {
return IntToString(ClipboardUtil::GetCFHDropFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFileDescriptorFormatType() {
return IntToString(ClipboardUtil::GetFileDescriptorFormat()->cfFormat);
}
// static
Clipboard::FormatType Clipboard::GetFileContentFormatZeroType() {
return IntToString(ClipboardUtil::GetFileContentFormatZero()->cfFormat);
}
// static
void Clipboard::DuplicateRemoteHandles(base::ProcessHandle process,
ObjectMap* objects) {
for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
++iter) {
if (iter->first == CBF_SMBITMAP) {
// There is a shared memory handle encoded on the first ObjectMapParam.
// Use it to open a local handle to the memory.
char* bitmap_data = &(iter->second[0].front());
base::SharedMemoryHandle* remote_bitmap_handle =
reinterpret_cast<base::SharedMemoryHandle*>(bitmap_data);
base::SharedMemory* bitmap = new base::SharedMemory(*remote_bitmap_handle,
false, process);
// We store the object where the remote handle was located so it can
// be retrieved by the UI thread (see WriteBitmapFromSharedMemory()).
iter->second[0].clear();
for (size_t i = 0; i < sizeof(bitmap); i++)
iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
}
}
}
// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
return IntToString(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat);
}
// static
void Clipboard::FreeData(unsigned int format, HANDLE data) {
if (format == CF_BITMAP)
::DeleteObject(static_cast<HBITMAP>(data));
else
::GlobalFree(data);
}
HWND Clipboard::GetClipboardWindow() const {
if (!clipboard_owner_ && create_window_) {
clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass",
L"ClipboardOwnerWindow",
0, 0, 0, 0, 0,
HWND_MESSAGE,
0, 0, 0);
}
return clipboard_owner_;
}

View File

@ -1,72 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <windows.h>
#include <ImageHlp.h>
#include <psapi.h>
#include "base/image_util.h"
#include "base/process_util.h"
// imagehlp.dll appears to ship in all win versions after Win95.
// nsylvain verified it is present in win2k.
// Using #pragma comment for dependency, instead of LoadLibrary/GetProcAddress.
#pragma comment(lib, "imagehlp.lib")
namespace image_util {
// ImageMetrics
ImageMetrics::ImageMetrics(HANDLE process) : process_(process) {
}
ImageMetrics::~ImageMetrics() {
}
bool ImageMetrics::GetDllImageSectionData(const std::string& loaded_dll_name,
ImageSectionsData* section_sizes) {
// Get a handle to the loaded DLL
HMODULE the_dll = GetModuleHandleA(loaded_dll_name.c_str());
char full_filename[MAX_PATH];
// Get image path
if (GetModuleFileNameExA(process_, the_dll, full_filename, MAX_PATH)) {
return GetImageSectionSizes(full_filename, section_sizes);
}
return false;
}
bool ImageMetrics::GetProcessImageSectionData(ImageSectionsData*
section_sizes) {
char exe_path[MAX_PATH];
// Get image path
if (GetModuleFileNameExA(process_, NULL, exe_path, MAX_PATH)) {
return GetImageSectionSizes(exe_path, section_sizes);
}
return false;
}
// private
bool ImageMetrics::GetImageSectionSizes(char* qualified_path,
ImageSectionsData* result) {
LOADED_IMAGE li;
// TODO (timsteele): There is no unicode version for MapAndLoad, hence
// why ansi functions are used in this class. Should we try and rewrite
// this call ourselves to be safe?
if (MapAndLoad(qualified_path, 0, &li, FALSE, TRUE)) {
IMAGE_SECTION_HEADER* section_header = li.Sections;
for (unsigned i = 0; i < li.NumberOfSections; i++, section_header++) {
std::string name(reinterpret_cast<char*>(section_header->Name));
ImageSectionData data(name, section_header->Misc.VirtualSize ?
section_header->Misc.VirtualSize :
section_header->SizeOfRawData);
// copy into result
result->push_back(data);
}
} else {
// map and load failed
return false;
}
UnMapAndLoad(&li);
return true;
}
} // namespace image_util

View File

@ -1,66 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file/namespace contains utility functions for gathering
// information about PE (Portable Executable) headers within
// images (dll's / exe's )
#ifndef BASE_IMAGE_UTIL_H_
#define BASE_IMAGE_UTIL_H_
#include <windows.h>
#include <vector>
#include "base/basictypes.h"
namespace image_util {
// Contains both the PE section name (.text, .reloc etc) and its size.
struct ImageSectionData {
ImageSectionData (const std::string& section_name, size_t section_size)
: name (section_name),
size_in_bytes(section_size) {
}
std::string name;
size_t size_in_bytes;
};
typedef std::vector<ImageSectionData> ImageSectionsData;
// Provides image statistics for modules of a specified process, or for the
// specified process' own executable file. To use, invoke CreateImageMetrics()
// to get an instance for a specified process, then access the information via
// methods.
class ImageMetrics {
public:
// Creates an ImageMetrics instance for given process owned by
// the caller.
explicit ImageMetrics(HANDLE process);
~ImageMetrics();
// Fills a vector of ImageSectionsData containing name/size info
// for every section found in the specified dll's PE section table.
// The DLL must be loaded by the process associated with this ImageMetrics
// instance.
bool GetDllImageSectionData(const std::string& loaded_dll_name,
ImageSectionsData* section_sizes);
// Fills a vector if ImageSectionsData containing name/size info
// for every section found in the executable file of the process
// associated with this ImageMetrics instance.
bool GetProcessImageSectionData(ImageSectionsData* section_sizes);
private:
// Helper for GetDllImageSectionData and GetProcessImageSectionData
bool GetImageSectionSizes(char* qualified_path, ImageSectionsData* result);
HANDLE process_;
DISALLOW_COPY_AND_ASSIGN(ImageMetrics);
};
} // namespace image_util
#endif // BASE_IMAGE_UTIL_H_

View File

@ -1,641 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/json_reader.h"
#include "base/float_util.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/values.h"
static const JSONReader::Token kInvalidToken(JSONReader::Token::INVALID_TOKEN,
0, 0);
static const int kStackLimit = 100;
namespace {
inline int HexToInt(wchar_t c) {
if ('0' <= c && c <= '9') {
return c - '0';
} else if ('A' <= c && c <= 'F') {
return c - 'A' + 10;
} else if ('a' <= c && c <= 'f') {
return c - 'a' + 10;
}
NOTREACHED();
return 0;
}
// A helper method for ParseNumberToken. It reads an int from the end of
// token. The method returns false if there is no valid integer at the end of
// the token.
bool ReadInt(JSONReader::Token& token, bool can_have_leading_zeros) {
wchar_t first = token.NextChar();
int len = 0;
// Read in more digits
wchar_t c = first;
while ('\0' != c && '0' <= c && c <= '9') {
++token.length;
++len;
c = token.NextChar();
}
// We need at least 1 digit.
if (len == 0)
return false;
if (!can_have_leading_zeros && len > 1 && '0' == first)
return false;
return true;
}
// A helper method for ParseStringToken. It reads |digits| hex digits from the
// token. If the sequence if digits is not valid (contains other characters),
// the method returns false.
bool ReadHexDigits(JSONReader::Token& token, int digits) {
for (int i = 1; i <= digits; ++i) {
wchar_t c = *(token.begin + token.length + i);
if ('\0' == c)
return false;
if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
('A' <= c && c <= 'F'))) {
return false;
}
}
token.length += digits;
return true;
}
} // anonymous namespace
const char* JSONReader::kBadRootElementType =
"Root value must be an array or object.";
const char* JSONReader::kInvalidEscape =
"Invalid escape sequence.";
const char* JSONReader::kSyntaxError =
"Syntax error.";
const char* JSONReader::kTrailingComma =
"Trailing comma not allowed.";
const char* JSONReader::kTooMuchNesting =
"Too much nesting.";
const char* JSONReader::kUnexpectedDataAfterRoot =
"Unexpected data after root element.";
const char* JSONReader::kUnsupportedEncoding =
"Unsupported encoding. JSON must be UTF-8.";
const char* JSONReader::kUnquotedDictionaryKey =
"Dictionary keys must be quoted.";
/* static */
Value* JSONReader::Read(const std::string& json,
bool allow_trailing_comma) {
return ReadAndReturnError(json, allow_trailing_comma, NULL);
}
/* static */
Value* JSONReader::ReadAndReturnError(const std::string& json,
bool allow_trailing_comma,
std::string *error_message_out) {
JSONReader reader = JSONReader();
Value* root = reader.JsonToValue(json, true, allow_trailing_comma);
if (root)
return root;
if (error_message_out)
*error_message_out = reader.error_message();
return NULL;
}
/* static */
std::string JSONReader::FormatErrorMessage(int line, int column,
const char* description) {
return StringPrintf("Line: %i, column: %i, %s",
line, column, description);
}
JSONReader::JSONReader()
: start_pos_(NULL), json_pos_(NULL), stack_depth_(0),
allow_trailing_comma_(false) {}
Value* JSONReader::JsonToValue(const std::string& json, bool check_root,
bool allow_trailing_comma) {
// The input must be in UTF-8.
if (!IsStringUTF8(json.c_str())) {
error_message_ = kUnsupportedEncoding;
return NULL;
}
// The conversion from UTF8 to wstring removes null bytes for us
// (a good thing).
std::wstring json_wide(UTF8ToWide(json));
start_pos_ = json_wide.c_str();
// When the input JSON string starts with a UTF-8 Byte-Order-Mark
// (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a Unicode
// BOM (U+FEFF). To avoid the JSONReader::BuildValue() function from
// mis-treating a Unicode BOM as an invalid character and returning NULL,
// skip a converted Unicode BOM if it exists.
if (!json_wide.empty() && start_pos_[0] == 0xFEFF) {
++start_pos_;
}
json_pos_ = start_pos_;
allow_trailing_comma_ = allow_trailing_comma;
stack_depth_ = 0;
error_message_.clear();
scoped_ptr<Value> root(BuildValue(check_root));
if (root.get()) {
if (ParseToken().type == Token::END_OF_INPUT) {
return root.release();
} else {
SetErrorMessage(kUnexpectedDataAfterRoot, json_pos_);
}
}
// Default to calling errors "syntax errors".
if (error_message_.empty())
SetErrorMessage(kSyntaxError, json_pos_);
return NULL;
}
Value* JSONReader::BuildValue(bool is_root) {
++stack_depth_;
if (stack_depth_ > kStackLimit) {
SetErrorMessage(kTooMuchNesting, json_pos_);
return NULL;
}
Token token = ParseToken();
// The root token must be an array or an object.
if (is_root && token.type != Token::OBJECT_BEGIN &&
token.type != Token::ARRAY_BEGIN) {
SetErrorMessage(kBadRootElementType, json_pos_);
return NULL;
}
scoped_ptr<Value> node;
switch (token.type) {
case Token::END_OF_INPUT:
case Token::INVALID_TOKEN:
return NULL;
case Token::NULL_TOKEN:
node.reset(Value::CreateNullValue());
break;
case Token::BOOL_TRUE:
node.reset(Value::CreateBooleanValue(true));
break;
case Token::BOOL_FALSE:
node.reset(Value::CreateBooleanValue(false));
break;
case Token::NUMBER:
node.reset(DecodeNumber(token));
if (!node.get())
return NULL;
break;
case Token::STRING:
node.reset(DecodeString(token));
if (!node.get())
return NULL;
break;
case Token::ARRAY_BEGIN:
{
json_pos_ += token.length;
token = ParseToken();
node.reset(new ListValue());
while (token.type != Token::ARRAY_END) {
Value* array_node = BuildValue(false);
if (!array_node)
return NULL;
static_cast<ListValue*>(node.get())->Append(array_node);
// After a list value, we expect a comma or the end of the list.
token = ParseToken();
if (token.type == Token::LIST_SEPARATOR) {
json_pos_ += token.length;
token = ParseToken();
// Trailing commas are invalid according to the JSON RFC, but some
// consumers need the parsing leniency, so handle accordingly.
if (token.type == Token::ARRAY_END) {
if (!allow_trailing_comma_) {
SetErrorMessage(kTrailingComma, json_pos_);
return NULL;
}
// Trailing comma OK, stop parsing the Array.
break;
}
} else if (token.type != Token::ARRAY_END) {
// Unexpected value after list value. Bail out.
return NULL;
}
}
if (token.type != Token::ARRAY_END) {
return NULL;
}
break;
}
case Token::OBJECT_BEGIN:
{
json_pos_ += token.length;
token = ParseToken();
node.reset(new DictionaryValue);
while (token.type != Token::OBJECT_END) {
if (token.type != Token::STRING) {
SetErrorMessage(kUnquotedDictionaryKey, json_pos_);
return NULL;
}
scoped_ptr<Value> dict_key_value(DecodeString(token));
if (!dict_key_value.get())
return NULL;
// Convert the key into a wstring.
std::wstring dict_key;
bool success = dict_key_value->GetAsString(&dict_key);
DCHECK(success);
json_pos_ += token.length;
token = ParseToken();
if (token.type != Token::OBJECT_PAIR_SEPARATOR)
return NULL;
json_pos_ += token.length;
token = ParseToken();
Value* dict_value = BuildValue(false);
if (!dict_value)
return NULL;
static_cast<DictionaryValue*>(node.get())->Set(dict_key, dict_value);
// After a key/value pair, we expect a comma or the end of the
// object.
token = ParseToken();
if (token.type == Token::LIST_SEPARATOR) {
json_pos_ += token.length;
token = ParseToken();
// Trailing commas are invalid according to the JSON RFC, but some
// consumers need the parsing leniency, so handle accordingly.
if (token.type == Token::OBJECT_END) {
if (!allow_trailing_comma_) {
SetErrorMessage(kTrailingComma, json_pos_);
return NULL;
}
// Trailing comma OK, stop parsing the Object.
break;
}
} else if (token.type != Token::OBJECT_END) {
// Unexpected value after last object value. Bail out.
return NULL;
}
}
if (token.type != Token::OBJECT_END)
return NULL;
break;
}
default:
// We got a token that's not a value.
return NULL;
}
json_pos_ += token.length;
--stack_depth_;
return node.release();
}
JSONReader::Token JSONReader::ParseNumberToken() {
// We just grab the number here. We validate the size in DecodeNumber.
// According to RFC4627, a valid number is: [minus] int [frac] [exp]
Token token(Token::NUMBER, json_pos_, 0);
wchar_t c = *json_pos_;
if ('-' == c) {
++token.length;
c = token.NextChar();
}
if (!ReadInt(token, false))
return kInvalidToken;
// Optional fraction part
c = token.NextChar();
if ('.' == c) {
++token.length;
if (!ReadInt(token, true))
return kInvalidToken;
c = token.NextChar();
}
// Optional exponent part
if ('e' == c || 'E' == c) {
++token.length;
c = token.NextChar();
if ('-' == c || '+' == c) {
++token.length;
c = token.NextChar();
}
if (!ReadInt(token, true))
return kInvalidToken;
}
return token;
}
Value* JSONReader::DecodeNumber(const Token& token) {
const std::wstring num_string(token.begin, token.length);
int num_int;
if (StringToInt(WideToUTF16Hack(num_string), &num_int))
return Value::CreateIntegerValue(num_int);
double num_double;
if (StringToDouble(WideToUTF16Hack(num_string), &num_double) &&
base::IsFinite(num_double))
return Value::CreateRealValue(num_double);
return NULL;
}
JSONReader::Token JSONReader::ParseStringToken() {
Token token(Token::STRING, json_pos_, 1);
wchar_t c = token.NextChar();
while ('\0' != c) {
if ('\\' == c) {
++token.length;
c = token.NextChar();
// Make sure the escaped char is valid.
switch (c) {
case 'x':
if (!ReadHexDigits(token, 2)) {
SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
return kInvalidToken;
}
break;
case 'u':
if (!ReadHexDigits(token, 4)) {
SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
return kInvalidToken;
}
break;
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
case 'v':
case '"':
break;
default:
SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
return kInvalidToken;
}
} else if ('"' == c) {
++token.length;
return token;
}
++token.length;
c = token.NextChar();
}
return kInvalidToken;
}
Value* JSONReader::DecodeString(const Token& token) {
std::wstring decoded_str;
decoded_str.reserve(token.length - 2);
for (int i = 1; i < token.length - 1; ++i) {
wchar_t c = *(token.begin + i);
if ('\\' == c) {
++i;
c = *(token.begin + i);
switch (c) {
case '"':
case '/':
case '\\':
decoded_str.push_back(c);
break;
case 'b':
decoded_str.push_back('\b');
break;
case 'f':
decoded_str.push_back('\f');
break;
case 'n':
decoded_str.push_back('\n');
break;
case 'r':
decoded_str.push_back('\r');
break;
case 't':
decoded_str.push_back('\t');
break;
case 'v':
decoded_str.push_back('\v');
break;
case 'x':
decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 4) +
HexToInt(*(token.begin + i + 2)));
i += 2;
break;
case 'u':
decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 12 ) +
(HexToInt(*(token.begin + i + 2)) << 8) +
(HexToInt(*(token.begin + i + 3)) << 4) +
HexToInt(*(token.begin + i + 4)));
i += 4;
break;
default:
// We should only have valid strings at this point. If not,
// ParseStringToken didn't do it's job.
NOTREACHED();
return NULL;
}
} else {
// Not escaped
decoded_str.push_back(c);
}
}
return Value::CreateStringValue(decoded_str);
}
JSONReader::Token JSONReader::ParseToken() {
static const std::wstring kNullString(L"null");
static const std::wstring kTrueString(L"true");
static const std::wstring kFalseString(L"false");
EatWhitespaceAndComments();
Token token(Token::INVALID_TOKEN, 0, 0);
switch (*json_pos_) {
case '\0':
token.type = Token::END_OF_INPUT;
break;
case 'n':
if (NextStringMatch(kNullString))
token = Token(Token::NULL_TOKEN, json_pos_, 4);
break;
case 't':
if (NextStringMatch(kTrueString))
token = Token(Token::BOOL_TRUE, json_pos_, 4);
break;
case 'f':
if (NextStringMatch(kFalseString))
token = Token(Token::BOOL_FALSE, json_pos_, 5);
break;
case '[':
token = Token(Token::ARRAY_BEGIN, json_pos_, 1);
break;
case ']':
token = Token(Token::ARRAY_END, json_pos_, 1);
break;
case ',':
token = Token(Token::LIST_SEPARATOR, json_pos_, 1);
break;
case '{':
token = Token(Token::OBJECT_BEGIN, json_pos_, 1);
break;
case '}':
token = Token(Token::OBJECT_END, json_pos_, 1);
break;
case ':':
token = Token(Token::OBJECT_PAIR_SEPARATOR, json_pos_, 1);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
token = ParseNumberToken();
break;
case '"':
token = ParseStringToken();
break;
}
return token;
}
bool JSONReader::NextStringMatch(const std::wstring& str) {
for (size_t i = 0; i < str.length(); ++i) {
if ('\0' == *json_pos_)
return false;
if (*(json_pos_ + i) != str[i])
return false;
}
return true;
}
void JSONReader::EatWhitespaceAndComments() {
while ('\0' != *json_pos_) {
switch (*json_pos_) {
case ' ':
case '\n':
case '\r':
case '\t':
++json_pos_;
break;
case '/':
// TODO(tc): This isn't in the RFC so it should be a parser flag.
if (!EatComment())
return;
break;
default:
// Not a whitespace char, just exit.
return;
}
}
}
bool JSONReader::EatComment() {
if ('/' != *json_pos_)
return false;
wchar_t next_char = *(json_pos_ + 1);
if ('/' == next_char) {
// Line comment, read until \n or \r
json_pos_ += 2;
while ('\0' != *json_pos_) {
switch (*json_pos_) {
case '\n':
case '\r':
++json_pos_;
return true;
default:
++json_pos_;
}
}
} else if ('*' == next_char) {
// Block comment, read until */
json_pos_ += 2;
while ('\0' != *json_pos_) {
switch (*json_pos_) {
case '*':
if ('/' == *(json_pos_ + 1)) {
json_pos_ += 2;
return true;
}
default:
++json_pos_;
}
}
} else {
return false;
}
return true;
}
void JSONReader::SetErrorMessage(const char* description,
const wchar_t* error_pos) {
int line_number = 1;
int column_number = 1;
// Figure out the line and column the error occured at.
for (const wchar_t* pos = start_pos_; pos != error_pos; ++pos) {
if (*pos == '\0') {
NOTREACHED();
return;
}
if (*pos == '\n') {
++line_number;
column_number = 1;
} else {
++column_number;
}
}
error_message_ = FormatErrorMessage(line_number, column_number, description);
}

View File

@ -1,186 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A JSON parser. Converts strings of JSON into a Value object (see
// base/values.h).
// http://www.ietf.org/rfc/rfc4627.txt?number=4627
//
// Known limitations/deviations from the RFC:
// - Only knows how to parse ints within the range of a signed 32 bit int and
// decimal numbers within a double.
// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16
// (BE or LE) and UTF-32 (BE or LE) as well.
// - We limit nesting to 100 levels to prevent stack overflow (this is allowed
// by the RFC).
// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data
// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input
// UTF-8 string for the JSONReader::JsonToValue() function may start with a
// UTF-8 BOM (0xEF, 0xBB, 0xBF).
// To avoid the function from mis-treating a UTF-8 BOM as an invalid
// character, the function skips a Unicode BOM at the beginning of the
// Unicode string (converted from the input UTF-8 string) before parsing it.
//
// TODO(tc): Add a parsing option to to relax object keys being wrapped in
// double quotes
// TODO(tc): Add an option to disable comment stripping
// TODO(aa): Consider making the constructor public and the static Read() method
// only a convenience for the common uses with more complex configuration going
// on the instance.
#ifndef BASE_JSON_READER_H_
#define BASE_JSON_READER_H_
#include <string>
#include "base/basictypes.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
class Value;
class JSONReader {
public:
// A struct to hold a JS token.
class Token {
public:
enum Type {
OBJECT_BEGIN, // {
OBJECT_END, // }
ARRAY_BEGIN, // [
ARRAY_END, // ]
STRING,
NUMBER,
BOOL_TRUE, // true
BOOL_FALSE, // false
NULL_TOKEN, // null
LIST_SEPARATOR, // ,
OBJECT_PAIR_SEPARATOR, // :
END_OF_INPUT,
INVALID_TOKEN,
};
Token(Type t, const wchar_t* b, int len)
: type(t), begin(b), length(len) {}
Type type;
// A pointer into JSONReader::json_pos_ that's the beginning of this token.
const wchar_t* begin;
// End should be one char past the end of the token.
int length;
// Get the character that's one past the end of this token.
wchar_t NextChar() {
return *(begin + length);
}
};
// Error messages that can be returned.
static const char* kBadRootElementType;
static const char* kInvalidEscape;
static const char* kSyntaxError;
static const char* kTrailingComma;
static const char* kTooMuchNesting;
static const char* kUnexpectedDataAfterRoot;
static const char* kUnsupportedEncoding;
static const char* kUnquotedDictionaryKey;
JSONReader();
// Reads and parses |json|, returning a Value. The caller owns the returned
// instance. If |json| is not a properly formed JSON string, returns NULL.
// If |allow_trailing_comma| is true, we will ignore trailing commas in
// objects and arrays even though this goes against the RFC.
static Value* Read(const std::string& json, bool allow_trailing_comma);
// Reads and parses |json| like Read(). |error_message_out| is optional. If
// specified and NULL is returned, |error_message_out| will be populated with
// a string describing the error. Otherwise, |error_message_out| is
// unmodified.
static Value* ReadAndReturnError(const std::string& json,
bool allow_trailing_comma,
std::string* error_message_out);
// Returns the error message if the last call to JsonToValue() failed. If the
// last call did not fail, returns a valid empty string.
std::string error_message() { return error_message_; }
// Reads and parses |json|, returning a Value. The caller owns the returned
// instance. If |json| is not a properly formed JSON string, returns NULL and
// a detailed error can be retrieved from |error_message()|.
// If |check_root| is true, we require that the root object be an object or
// array. Otherwise, it can be any valid JSON type.
// If |allow_trailing_comma| is true, we will ignore trailing commas in
// objects and arrays even though this goes against the RFC.
Value* JsonToValue(const std::string& json, bool check_root,
bool allow_trailing_comma);
private:
static std::string FormatErrorMessage(int line, int column,
const char* description);
DISALLOW_EVIL_CONSTRUCTORS(JSONReader);
FRIEND_TEST(JSONReaderTest, Reading);
FRIEND_TEST(JSONReaderTest, ErrorMessages);
// Recursively build Value. Returns NULL if we don't have a valid JSON
// string. If |is_root| is true, we verify that the root element is either
// an object or an array.
Value* BuildValue(bool is_root);
// Parses a sequence of characters into a Token::NUMBER. If the sequence of
// characters is not a valid number, returns a Token::INVALID_TOKEN. Note
// that DecodeNumber is used to actually convert from a string to an
// int/double.
Token ParseNumberToken();
// Try and convert the substring that token holds into an int or a double. If
// we can (ie., no overflow), return the value, else return NULL.
Value* DecodeNumber(const Token& token);
// Parses a sequence of characters into a Token::STRING. If the sequence of
// characters is not a valid string, returns a Token::INVALID_TOKEN. Note
// that DecodeString is used to actually decode the escaped string into an
// actual wstring.
Token ParseStringToken();
// Convert the substring into a value string. This should always succeed
// (otherwise ParseStringToken would have failed).
Value* DecodeString(const Token& token);
// Grabs the next token in the JSON stream. This does not increment the
// stream so it can be used to look ahead at the next token.
Token ParseToken();
// Increments |json_pos_| past leading whitespace and comments.
void EatWhitespaceAndComments();
// If |json_pos_| is at the start of a comment, eat it, otherwise, returns
// false.
bool EatComment();
// Checks if |json_pos_| matches str.
bool NextStringMatch(const std::wstring& str);
// Creates the error message that will be returned to the caller. The current
// line and column are determined and added into the final message.
void SetErrorMessage(const char* description, const wchar_t* error_pos);
// Pointer to the starting position in the input string.
const wchar_t* start_pos_;
// Pointer to the current position in the input string.
const wchar_t* json_pos_;
// Used to keep track of how many nested lists/dicts there are.
int stack_depth_;
// A parser flag that allows trailing commas in objects and arrays.
bool allow_trailing_comma_;
// Contains the error message for the last call to JsonToValue(), if any.
std::string error_message_;
};
#endif // BASE_JSON_READER_H_

View File

@ -1,492 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/json_reader.h"
#include "base/scoped_ptr.h"
#include "base/values.h"
#include "build/build_config.h"
TEST(JSONReaderTest, Reading) {
// some whitespace checking
scoped_ptr<Value> root;
root.reset(JSONReader().JsonToValue(" null ", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_NULL));
// Invalid JSON string
root.reset(JSONReader().JsonToValue("nu", false, false));
ASSERT_FALSE(root.get());
// Simple bool
root.reset(JSONReader().JsonToValue("true ", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
// Test number formats
root.reset(JSONReader().JsonToValue("43", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
int int_val = 0;
ASSERT_TRUE(root->GetAsInteger(&int_val));
ASSERT_EQ(43, int_val);
// According to RFC4627, oct, hex, and leading zeros are invalid JSON.
root.reset(JSONReader().JsonToValue("043", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("0x43", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("00", false, false));
ASSERT_FALSE(root.get());
// Test 0 (which needs to be special cased because of the leading zero
// clause).
root.reset(JSONReader().JsonToValue("0", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
int_val = 1;
ASSERT_TRUE(root->GetAsInteger(&int_val));
ASSERT_EQ(0, int_val);
// Numbers that overflow ints should succeed, being internally promoted to
// storage as doubles
root.reset(JSONReader().JsonToValue("2147483648", false, false));
ASSERT_TRUE(root.get());
double real_val;
#ifdef ARCH_CPU_32_BITS
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(2147483648.0, real_val);
#else
ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
int_val = 0;
ASSERT_TRUE(root->GetAsInteger(&int_val));
ASSERT_EQ(2147483648, int_val);
#endif
root.reset(JSONReader().JsonToValue("-2147483649", false, false));
ASSERT_TRUE(root.get());
#ifdef ARCH_CPU_32_BITS
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(-2147483649.0, real_val);
#else
ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
int_val = 0;
ASSERT_TRUE(root->GetAsInteger(&int_val));
ASSERT_EQ(-2147483649, int_val);
#endif
// Parse a double
root.reset(JSONReader().JsonToValue("43.1", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(43.1, real_val);
root.reset(JSONReader().JsonToValue("4.3e-1", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(.43, real_val);
root.reset(JSONReader().JsonToValue("2.1e0", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(2.1, real_val);
root.reset(JSONReader().JsonToValue("2.1e+0001", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(21.0, real_val);
root.reset(JSONReader().JsonToValue("0.01", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(0.01, real_val);
root.reset(JSONReader().JsonToValue("1.00", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
ASSERT_TRUE(root->GetAsReal(&real_val));
ASSERT_DOUBLE_EQ(1.0, real_val);
// Fractional parts must have a digit before and after the decimal point.
root.reset(JSONReader().JsonToValue("1.", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue(".1", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("1.e10", false, false));
ASSERT_FALSE(root.get());
// Exponent must have a digit following the 'e'.
root.reset(JSONReader().JsonToValue("1e", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("1E", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("1e1.", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("1e1.0", false, false));
ASSERT_FALSE(root.get());
// INF/-INF/NaN are not valid
root.reset(JSONReader().JsonToValue("1e1000", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("-1e1000", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("NaN", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("nan", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("inf", false, false));
ASSERT_FALSE(root.get());
// Invalid number formats
root.reset(JSONReader().JsonToValue("4.3.1", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("4e3.1", false, false));
ASSERT_FALSE(root.get());
// Test string parser
root.reset(JSONReader().JsonToValue("\"hello world\"", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
std::wstring str_val;
ASSERT_TRUE(root->GetAsString(&str_val));
ASSERT_EQ(L"hello world", str_val);
// Empty string
root.reset(JSONReader().JsonToValue("\"\"", false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
str_val.clear();
ASSERT_TRUE(root->GetAsString(&str_val));
ASSERT_EQ(L"", str_val);
// Test basic string escapes
root.reset(JSONReader().JsonToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"",
false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
str_val.clear();
ASSERT_TRUE(root->GetAsString(&str_val));
ASSERT_EQ(L" \"\\/\b\f\n\r\t\v", str_val);
// Test hex and unicode escapes including the null character.
root.reset(JSONReader().JsonToValue("\"\\x41\\x00\\u1234\"", false,
false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
str_val.clear();
ASSERT_TRUE(root->GetAsString(&str_val));
ASSERT_EQ(std::wstring(L"A\0\x1234", 3), str_val);
// Test invalid strings
root.reset(JSONReader().JsonToValue("\"no closing quote", false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("\"\\z invalid escape char\"", false,
false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("\"\\xAQ invalid hex code\"", false,
false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("not enough hex chars\\x1\"", false,
false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("\"not enough escape chars\\u123\"",
false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("\"extra backslash at end of input\\\"",
false, false));
ASSERT_FALSE(root.get());
// Basic array
root.reset(JSONReader::Read("[true, false, null]", false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
ListValue* list = static_cast<ListValue*>(root.get());
ASSERT_EQ(3U, list->GetSize());
// Test with trailing comma. Should be parsed the same as above.
scoped_ptr<Value> root2;
root2.reset(JSONReader::Read("[true, false, null, ]", true));
EXPECT_TRUE(root->Equals(root2.get()));
// Empty array
root.reset(JSONReader::Read("[]", false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
list = static_cast<ListValue*>(root.get());
ASSERT_EQ(0U, list->GetSize());
// Nested arrays
root.reset(JSONReader::Read("[[true], [], [false, [], [null]], null]",
false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
list = static_cast<ListValue*>(root.get());
ASSERT_EQ(4U, list->GetSize());
// Lots of trailing commas.
root2.reset(JSONReader::Read("[[true], [], [false, [], [null, ] , ], null,]",
true));
EXPECT_TRUE(root->Equals(root2.get()));
// Invalid, missing close brace.
root.reset(JSONReader::Read("[[true], [], [false, [], [null]], null", false));
ASSERT_FALSE(root.get());
// Invalid, too many commas
root.reset(JSONReader::Read("[true,, null]", false));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("[true,, null]", true));
ASSERT_FALSE(root.get());
// Invalid, no commas
root.reset(JSONReader::Read("[true null]", false));
ASSERT_FALSE(root.get());
// Invalid, trailing comma
root.reset(JSONReader::Read("[true,]", false));
ASSERT_FALSE(root.get());
// Valid if we set |allow_trailing_comma| to true.
root.reset(JSONReader::Read("[true,]", true));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
list = static_cast<ListValue*>(root.get());
EXPECT_EQ(1U, list->GetSize());
Value* tmp_value = NULL;
ASSERT_TRUE(list->Get(0, &tmp_value));
EXPECT_TRUE(tmp_value->IsType(Value::TYPE_BOOLEAN));
bool bool_value = false;
ASSERT_TRUE(tmp_value->GetAsBoolean(&bool_value));
EXPECT_TRUE(bool_value);
// Don't allow empty elements, even if |allow_trailing_comma| is
// true.
root.reset(JSONReader::Read("[,]", true));
EXPECT_FALSE(root.get());
root.reset(JSONReader::Read("[true,,]", true));
EXPECT_FALSE(root.get());
root.reset(JSONReader::Read("[,true,]", true));
EXPECT_FALSE(root.get());
root.reset(JSONReader::Read("[true,,false]", true));
EXPECT_FALSE(root.get());
// Test objects
root.reset(JSONReader::Read("{}", false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
root.reset(JSONReader::Read(
"{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }",
false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
DictionaryValue* dict_val = static_cast<DictionaryValue*>(root.get());
real_val = 0.0;
ASSERT_TRUE(dict_val->GetReal(L"number", &real_val));
ASSERT_DOUBLE_EQ(9.87654321, real_val);
Value* null_val = NULL;
ASSERT_TRUE(dict_val->Get(L"null", &null_val));
ASSERT_TRUE(null_val->IsType(Value::TYPE_NULL));
str_val.clear();
ASSERT_TRUE(dict_val->GetString(L"S", &str_val));
ASSERT_EQ(L"str", str_val);
root2.reset(JSONReader::Read(
"{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", }", true));
EXPECT_TRUE(root->Equals(root2.get()));
// Test nesting
root.reset(JSONReader::Read(
"{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}", false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
dict_val = static_cast<DictionaryValue*>(root.get());
DictionaryValue* inner_dict = NULL;
ASSERT_TRUE(dict_val->GetDictionary(L"inner", &inner_dict));
ListValue* inner_array = NULL;
ASSERT_TRUE(inner_dict->GetList(L"array", &inner_array));
ASSERT_EQ(1U, inner_array->GetSize());
bool_value = true;
ASSERT_TRUE(dict_val->GetBoolean(L"false", &bool_value));
ASSERT_FALSE(bool_value);
inner_dict = NULL;
ASSERT_TRUE(dict_val->GetDictionary(L"d", &inner_dict));
root2.reset(JSONReader::Read(
"{\"inner\": {\"array\":[true] , },\"false\":false,\"d\":{},}", true));
EXPECT_TRUE(root->Equals(root2.get()));
// Invalid, no closing brace
root.reset(JSONReader::Read("{\"a\": true", false));
ASSERT_FALSE(root.get());
// Invalid, keys must be quoted
root.reset(JSONReader::Read("{foo:true}", false));
ASSERT_FALSE(root.get());
// Invalid, trailing comma
root.reset(JSONReader::Read("{\"a\":true,}", false));
ASSERT_FALSE(root.get());
// Invalid, too many commas
root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}", false));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}", true));
ASSERT_FALSE(root.get());
// Invalid, no separator
root.reset(JSONReader::Read("{\"a\" \"b\"}", false));
ASSERT_FALSE(root.get());
// Invalid, lone comma.
root.reset(JSONReader::Read("{,}", false));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("{,}", true));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("{\"a\":true,,}", true));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("{,\"a\":true}", true));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}", true));
ASSERT_FALSE(root.get());
// Test stack overflow
std::string evil(1000000, '[');
evil.append(std::string(1000000, ']'));
root.reset(JSONReader::Read(evil, false));
ASSERT_FALSE(root.get());
// A few thousand adjacent lists is fine.
std::string not_evil("[");
not_evil.reserve(15010);
for (int i = 0; i < 5000; ++i) {
not_evil.append("[],");
}
not_evil.append("[]]");
root.reset(JSONReader::Read(not_evil, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
list = static_cast<ListValue*>(root.get());
ASSERT_EQ(5001U, list->GetSize());
// Test utf8 encoded input
root.reset(JSONReader().JsonToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"",
false, false));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
str_val.clear();
ASSERT_TRUE(root->GetAsString(&str_val));
ASSERT_EQ(L"\x7f51\x9875", str_val);
// Test invalid utf8 encoded input
root.reset(JSONReader().JsonToValue("\"345\xb0\xa1\xb0\xa2\"",
false, false));
ASSERT_FALSE(root.get());
root.reset(JSONReader().JsonToValue("\"123\xc0\x81\"",
false, false));
ASSERT_FALSE(root.get());
// Test invalid root objects.
root.reset(JSONReader::Read("null", false));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("true", false));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("10", false));
ASSERT_FALSE(root.get());
root.reset(JSONReader::Read("\"root\"", false));
ASSERT_FALSE(root.get());
}
TEST(JSONReaderTest, ErrorMessages) {
// Error strings should not be modified in case of success.
std::string error_message;
scoped_ptr<Value> root;
root.reset(JSONReader::ReadAndReturnError("[42]", false, &error_message));
EXPECT_TRUE(error_message.empty());
// Test line and column counting
const char* big_json = "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]";
// error here --------------------------------^
root.reset(JSONReader::ReadAndReturnError(big_json, false, &error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(5, 9, JSONReader::kSyntaxError),
error_message);
// Test each of the error conditions
root.reset(JSONReader::ReadAndReturnError("{},{}", false, &error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 3,
JSONReader::kUnexpectedDataAfterRoot), error_message);
std::string nested_json;
for (int i = 0; i < 101; ++i) {
nested_json.insert(nested_json.begin(), '[');
nested_json.append(1, ']');
}
root.reset(JSONReader::ReadAndReturnError(nested_json, false,
&error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 101, JSONReader::kTooMuchNesting),
error_message);
root.reset(JSONReader::ReadAndReturnError("42", false, &error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 1,
JSONReader::kBadRootElementType), error_message);
root.reset(JSONReader::ReadAndReturnError("[1,]", false, &error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 4, JSONReader::kTrailingComma),
error_message);
root.reset(JSONReader::ReadAndReturnError("{foo:\"bar\"}", false,
&error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 2,
JSONReader::kUnquotedDictionaryKey), error_message);
root.reset(JSONReader::ReadAndReturnError("{\"foo\":\"bar\",}", false,
&error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 14, JSONReader::kTrailingComma),
error_message);
root.reset(JSONReader::ReadAndReturnError("[nu]", false, &error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 2, JSONReader::kSyntaxError),
error_message);
root.reset(JSONReader::ReadAndReturnError("[\"xxx\\xq\"]", false,
&error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
error_message);
root.reset(JSONReader::ReadAndReturnError("[\"xxx\\uq\"]", false,
&error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
error_message);
root.reset(JSONReader::ReadAndReturnError("[\"xxx\\q\"]", false,
&error_message));
EXPECT_FALSE(root.get());
EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
error_message);
}

View File

@ -1,175 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/json_writer.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/values.h"
#include "base/string_escape.h"
const char kPrettyPrintLineEnding[] = "\r\n";
/* static */
void JSONWriter::Write(const Value* const node, bool pretty_print,
std::string* json) {
json->clear();
// Is there a better way to estimate the size of the output?
json->reserve(1024);
JSONWriter writer(pretty_print, json);
writer.BuildJSONString(node, 0);
if (pretty_print)
json->append(kPrettyPrintLineEnding);
}
JSONWriter::JSONWriter(bool pretty_print, std::string* json)
: json_string_(json),
pretty_print_(pretty_print) {
DCHECK(json);
}
void JSONWriter::BuildJSONString(const Value* const node, int depth) {
switch(node->GetType()) {
case Value::TYPE_NULL:
json_string_->append("null");
break;
case Value::TYPE_BOOLEAN:
{
bool value;
bool result = node->GetAsBoolean(&value);
DCHECK(result);
json_string_->append(value ? "true" : "false");
break;
}
case Value::TYPE_INTEGER:
{
int value;
bool result = node->GetAsInteger(&value);
DCHECK(result);
StringAppendF(json_string_, "%d", value);
break;
}
case Value::TYPE_REAL:
{
double value;
bool result = node->GetAsReal(&value);
DCHECK(result);
std::string real = DoubleToString(value);
// Ensure that the number has a .0 if there's no decimal or 'e'. This
// makes sure that when we read the JSON back, it's interpreted as a
// real rather than an int.
if (real.find('.') == std::string::npos &&
real.find('e') == std::string::npos &&
real.find('E') == std::string::npos) {
real.append(".0");
}
// The JSON spec requires that non-integer values in the range (-1,1)
// have a zero before the decimal point - ".52" is not valid, "0.52" is.
if (real[0] == '.') {
real.insert(0, "0");
} else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
// "-.1" bad "-0.1" good
real.insert(1, "0");
}
json_string_->append(real);
break;
}
case Value::TYPE_STRING:
{
std::wstring value;
bool result = node->GetAsString(&value);
DCHECK(result);
AppendQuotedString(value);
break;
}
case Value::TYPE_LIST:
{
json_string_->append("[");
if (pretty_print_)
json_string_->append(" ");
const ListValue* list = static_cast<const ListValue*>(node);
for (size_t i = 0; i < list->GetSize(); ++i) {
if (i != 0) {
json_string_->append(",");
if (pretty_print_)
json_string_->append(" ");
}
Value* value = NULL;
bool result = list->Get(i, &value);
DCHECK(result);
BuildJSONString(value, depth);
}
if (pretty_print_)
json_string_->append(" ");
json_string_->append("]");
break;
}
case Value::TYPE_DICTIONARY:
{
json_string_->append("{");
if (pretty_print_)
json_string_->append(kPrettyPrintLineEnding);
const DictionaryValue* dict =
static_cast<const DictionaryValue*>(node);
for (DictionaryValue::key_iterator key_itr = dict->begin_keys();
key_itr != dict->end_keys();
++key_itr) {
if (key_itr != dict->begin_keys()) {
json_string_->append(",");
if (pretty_print_)
json_string_->append(kPrettyPrintLineEnding);
}
Value* value = NULL;
bool result = dict->Get(*key_itr, &value);
DCHECK(result);
if (pretty_print_)
IndentLine(depth + 1);
AppendQuotedString(*key_itr);
if (pretty_print_) {
json_string_->append(": ");
} else {
json_string_->append(":");
}
BuildJSONString(value, depth + 1);
}
if (pretty_print_) {
json_string_->append(kPrettyPrintLineEnding);
IndentLine(depth);
json_string_->append("}");
} else {
json_string_->append("}");
}
break;
}
default:
// TODO(jhughes): handle TYPE_BINARY
NOTREACHED() << "unknown json type";
}
}
void JSONWriter::AppendQuotedString(const std::wstring& str) {
string_escape::JavascriptDoubleQuote(WideToUTF16Hack(str), true,
json_string_);
}
void JSONWriter::IndentLine(int depth) {
// It may be faster to keep an indent string so we don't have to keep
// reallocating.
json_string_->append(std::string(depth * 3, ' '));
}

View File

@ -1,47 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_JSON_WRITER_H_
#define BASE_JSON_WRITER_H_
#include <string>
#include "base/basictypes.h"
class Value;
class JSONWriter {
public:
// Given a root node, generates a JSON string and puts it into |json|.
// If |pretty_print| is true, return a slightly nicer formated json string
// (pads with whitespace to help readability). If |pretty_print| is false,
// we try to generate as compact a string as possible.
// TODO(tc): Should we generate json if it would be invalid json (e.g.,
// |node| is not a DictionaryValue/ListValue or if there are inf/-inf float
// values)?
static void Write(const Value* const node, bool pretty_print,
std::string* json);
private:
JSONWriter(bool pretty_print, std::string* json);
// Called recursively to build the JSON string. Whe completed, value is
// json_string_ will contain the JSON.
void BuildJSONString(const Value* const node, int depth);
// Appends a quoted, escaped, version of str to json_string_.
void AppendQuotedString(const std::wstring& str);
// Adds space to json_string_ for the indent level.
void IndentLine(int depth);
// Where we write JSON data as we generate it.
std::string* json_string_;
bool pretty_print_;
DISALLOW_COPY_AND_ASSIGN(JSONWriter);
};
#endif // BASE_JSON_WRITER_H_

View File

@ -1,68 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/json_writer.h"
#include "base/values.h"
TEST(JSONWriterTest, Writing) {
// Test null
Value* root = Value::CreateNullValue();
std::string output_js;
JSONWriter::Write(root, false, &output_js);
ASSERT_EQ("null", output_js);
delete root;
// Test empty dict
root = new DictionaryValue;
JSONWriter::Write(root, false, &output_js);
ASSERT_EQ("{}", output_js);
delete root;
// Test empty list
root = new ListValue;
JSONWriter::Write(root, false, &output_js);
ASSERT_EQ("[]", output_js);
delete root;
// Test Real values should always have a decimal or an 'e'.
root = Value::CreateRealValue(1.0);
JSONWriter::Write(root, false, &output_js);
ASSERT_EQ("1.0", output_js);
delete root;
// Test Real values in the the range (-1, 1) must have leading zeros
root = Value::CreateRealValue(0.2);
JSONWriter::Write(root, false, &output_js);
ASSERT_EQ("0.2", output_js);
delete root;
// Test Real values in the the range (-1, 1) must have leading zeros
root = Value::CreateRealValue(-0.8);
JSONWriter::Write(root, false, &output_js);
ASSERT_EQ("-0.8", output_js);
delete root;
// Writer unittests like empty list/dict nesting,
// list list nesting, etc.
DictionaryValue root_dict;
ListValue* list = new ListValue;
root_dict.Set(L"list", list);
DictionaryValue* inner_dict = new DictionaryValue;
list->Append(inner_dict);
inner_dict->SetInteger(L"inner int", 10);
ListValue* inner_list = new ListValue;
list->Append(inner_list);
list->Append(Value::CreateBooleanValue(true));
JSONWriter::Write(&root_dict, false, &output_js);
ASSERT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js);
JSONWriter::Write(&root_dict, true, &output_js);
ASSERT_EQ("{\r\n"
" \"list\": [ {\r\n"
" \"inner int\": 10\r\n"
" }, [ ], true ]\r\n"
"}\r\n",
output_js);
}

View File

@ -1,48 +0,0 @@
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_ACCESSIBILITY_TYPES_H_
#define CHROME_COMMON_ACCESSIBILITY_TYPES_H_
////////////////////////////////////////////////////////////////////////////////
//
// AccessibilityTypes
//
// Provides enumerations used to preserve platform-independence in accessibility
// functions used in various Views, both in Browser\Views and Views.
//
////////////////////////////////////////////////////////////////////////////////
class AccessibilityTypes {
public:
// This defines an enumeration of the supported accessibility roles in our
// Views (e.g. used in View::GetAccessibleRole). Any interface using roles
// must provide a conversion to its own roles (see e.g.
// ViewAccessibility::get_accRole and ViewAccessibility::MSAARole).
enum Role {
ROLE_APPLICATION,
ROLE_BUTTONDROPDOWN,
ROLE_CLIENT,
ROLE_GROUPING,
ROLE_PAGETAB,
ROLE_PUSHBUTTON,
ROLE_TEXT,
ROLE_TOOLBAR
};
// This defines an enumeration of the supported accessibility roles in our
// Views (e.g. used in View::GetAccessibleState). Any interface using roles
// must provide a conversion to its own roles (see e.g.
// ViewAccessibility::get_accState and ViewAccessibility::MSAAState).
enum State {
STATE_HASPOPUP,
STATE_READONLY
};
private:
// Do not instantiate this class.
AccessibilityTypes() {}
~AccessibilityTypes() {}
};
#endif // CHROME_COMMON_ACCESSIBILITY_TYPES_H_

View File

@ -1,84 +0,0 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/app_cache/app_cache_context_impl.h"
#include "base/logging.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/child_thread.h"
#include "googleurl/src/gurl.h"
IDMap<AppCacheContextImpl> AppCacheContextImpl::all_contexts;
// static
AppCacheContextImpl* AppCacheContextImpl::FromContextId(int id) {
return all_contexts.Lookup(id);
}
AppCacheContextImpl::AppCacheContextImpl(IPC::Message::Sender *sender)
: context_id_(kNoAppCacheContextId),
app_cache_id_(kUnknownAppCacheId),
pending_select_request_id_(0),
sender_(sender) {
DCHECK(sender_);
}
AppCacheContextImpl::~AppCacheContextImpl() {
UnInitializeContext();
}
void AppCacheContextImpl::Initialize(ContextType context_type,
WebAppCacheContext *parent) {
DCHECK(context_id_ == kNoAppCacheContextId);
DCHECK(((context_type == MAIN_FRAME) && !parent) ||
((context_type != MAIN_FRAME) && parent));
context_id_ = all_contexts.Add(this);
CHECK(context_id_ != kNoAppCacheContextId);
sender_->Send(new AppCacheMsg_ContextCreated(context_type,
context_id_,
parent ? parent->GetContextID()
: kNoAppCacheContextId));
}
void AppCacheContextImpl::UnInitializeContext() {
if (context_id_ != kNoAppCacheContextId) {
sender_->Send(new AppCacheMsg_ContextDestroyed(context_id_));
all_contexts.Remove(context_id_);
context_id_ = kNoAppCacheContextId;
}
}
void AppCacheContextImpl::SelectAppCacheWithoutManifest(
const GURL &document_url,
int64_t cache_document_was_loaded_from) {
DCHECK(context_id_ != kNoAppCacheContextId);
app_cache_id_ = kUnknownAppCacheId; // unknown until we get a response
sender_->Send(new AppCacheMsg_SelectAppCache(
context_id_, ++pending_select_request_id_,
document_url, cache_document_was_loaded_from,
GURL::EmptyGURL()));
}
void AppCacheContextImpl::SelectAppCacheWithManifest(
const GURL &document_url,
int64_t cache_document_was_loaded_from,
const GURL &manifest_url) {
DCHECK(context_id_ != kNoAppCacheContextId);
app_cache_id_ = kUnknownAppCacheId; // unknown until we get a response
sender_->Send(new AppCacheMsg_SelectAppCache(
context_id_, ++pending_select_request_id_,
document_url, cache_document_was_loaded_from,
manifest_url));
}
void AppCacheContextImpl::OnAppCacheSelected(int select_request_id,
int64_t app_cache_id) {
if (select_request_id == pending_select_request_id_) {
DCHECK(app_cache_id_ == kUnknownAppCacheId);
DCHECK(app_cache_id != kUnknownAppCacheId);
app_cache_id_ = app_cache_id;
}
}

View File

@ -1,48 +0,0 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_APP_CACHE_APP_CACHE_CONTEXT_IMPL_H_
#define CHROME_COMMON_APP_CACHE_APP_CACHE_CONTEXT_IMPL_H_
#include "base/id_map.h"
#include "chrome/common/ipc_message.h"
#include "webkit/glue/webappcachecontext.h"
// A concrete implemenation of WebAppCacheContext for use in a child process.
class AppCacheContextImpl : public WebAppCacheContext {
public:
// Returns the context having given id or NULL if there is no such context.
static AppCacheContextImpl* FromContextId(int id);
AppCacheContextImpl(IPC::Message::Sender* sender);
virtual ~AppCacheContextImpl();
// WebAppCacheContext implementation
virtual int GetContextID() { return context_id_; }
virtual int64_t GetAppCacheID() { return app_cache_id_; }
virtual void Initialize(WebAppCacheContext::ContextType context_type,
WebAppCacheContext* opt_parent);
virtual void SelectAppCacheWithoutManifest(
const GURL& document_url,
int64_t cache_document_was_loaded_from);
virtual void SelectAppCacheWithManifest(
const GURL& document_url,
int64_t cache_document_was_loaded_from,
const GURL& manifest_url);
// Called by AppCacheDispatcher when the browser has selected an appcache.
void OnAppCacheSelected(int select_request_id, int64_t app_cache_id);
private:
void UnInitializeContext();
int context_id_;
int64_t app_cache_id_;
int pending_select_request_id_;
IPC::Message::Sender* sender_;
static IDMap<AppCacheContextImpl> all_contexts;
};
#endif // CHROME_COMMON_APP_CACHE_APP_CACHE_CONTEXT_IMPL_H_

View File

@ -1,26 +0,0 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/app_cache/app_cache_dispatcher.h"
#include "chrome/common/app_cache/app_cache_context_impl.h"
#include "chrome/common/render_messages.h"
bool AppCacheDispatcher::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(AppCacheDispatcher, msg)
IPC_MESSAGE_HANDLER(AppCacheMsg_AppCacheSelected, OnAppCacheSelected)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void AppCacheDispatcher::OnAppCacheSelected(int context_id,
int select_request_id,
int64_t app_cache_id) {
AppCacheContextImpl *context = AppCacheContextImpl::FromContextId(context_id);
if (context) {
context->OnAppCacheSelected(select_request_id, app_cache_id);
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_H_
#define CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_H_
#include "base/basictypes.h"
#include "chrome/common/ipc_message.h"
// Dispatches app cache related messages sent to a child process from the
// main browser process. There is one instance per child process. Messages
// are dispatched on the main child thread. The ChildThread base class
// creates an instance and delegates calls to it.
class AppCacheDispatcher {
public:
bool OnMessageReceived(const IPC::Message& msg);
private:
// AppCacheContextImpl related messages
void OnAppCacheSelected(int context_id,
int select_request_id,
int64_t app_cache_id);
};
#endif // CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_H_

View File

@ -1,60 +0,0 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/app_cache/app_cache_dispatcher_host.h"
#include "chrome/common/render_messages.h"
AppCacheDispatcherHost::~AppCacheDispatcherHost() {
if (sender_) {
// TODO(michaeln): plumb to request_context_->app_cache_service
// to remove contexts for the child process that is going away.
}
}
void AppCacheDispatcherHost::Initialize(IPC::Message::Sender* sender) {
DCHECK(sender);
sender_ = sender;
// TODO(michaeln): plumb to request_context_->app_cache_service to
// tell it about this child process coming into existance
}
bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& msg,
bool *msg_ok) {
DCHECK(sender_);
*msg_ok = true;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(AppCacheDispatcherHost, msg, *msg_ok)
IPC_MESSAGE_HANDLER(AppCacheMsg_ContextCreated, OnContextCreated);
IPC_MESSAGE_HANDLER(AppCacheMsg_ContextDestroyed, OnContextDestroyed);
IPC_MESSAGE_HANDLER(AppCacheMsg_SelectAppCache, OnSelectAppCache);
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX()
return handled;
}
void AppCacheDispatcherHost::OnContextCreated(
WebAppCacheContext::ContextType context_type,
int context_id,
int opt_parent_id) {
// TODO(michaeln): implement me, plumb to request_context->app_cache_service
DCHECK(context_id != WebAppCacheContext::kNoAppCacheContextId);
}
void AppCacheDispatcherHost::OnContextDestroyed(int context_id) {
// TODO(michaeln): implement me, plumb to request_context->app_cache_service
DCHECK(context_id != WebAppCacheContext::kNoAppCacheContextId);
}
void AppCacheDispatcherHost::OnSelectAppCache(
int context_id,
int select_request_id,
const GURL& document_url,
int64_t cache_document_was_loaded_from,
const GURL& opt_manifest_url) {
// TODO(michaeln): implement me, plumb to request_context->app_cache_service
DCHECK(context_id != WebAppCacheContext::kNoAppCacheContextId);
Send(new AppCacheMsg_AppCacheSelected(context_id, select_request_id,
WebAppCacheContext::kNoAppCacheId));
}

View File

@ -1,43 +0,0 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_HOST_H_
#define CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_HOST_H_
#include "base/id_map.h"
#include "chrome/common/ipc_message.h"
#include "webkit/glue/webappcachecontext.h"
class GURL;
// Handles app cache related messages sent to the main browser process from
// its child processes. There is a distinct host for each child process.
// Messages are handled on the IO thread. The ResourceMessageFilter creates
// an instance and delegates calls to it.
class AppCacheDispatcherHost {
public:
AppCacheDispatcherHost() : sender_(NULL) {}
~AppCacheDispatcherHost();
void Initialize(IPC::Message::Sender* sender);
bool OnMessageReceived(const IPC::Message& msg, bool* msg_is_ok);
private:
// AppCacheContextImpl related messages
void OnContextCreated(WebAppCacheContext::ContextType context_type,
int context_id, int opt_parent_id);
void OnContextDestroyed(int context_id);
void OnSelectAppCache(int context_id,
int select_request_id,
const GURL& document_url,
int64_t cache_document_was_loaded_from,
const GURL& opt_manifest_url);
bool Send(IPC::Message* msg) {
return sender_->Send(msg);
}
IPC::Message::Sender* sender_;
};
#endif // CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_HOST_H_

Some files were not shown because too many files have changed in this diff Show More