Merge mozilla-central into cedar

This commit is contained in:
Ehsan Akhgari 2011-03-23 18:29:19 -04:00
commit f2fe50481f
252 changed files with 42185 additions and 11273 deletions

View File

@ -123,6 +123,10 @@ https://expired.example.com:443 privileged,cert=expired
https://requestclientcert.example.com:443 privileged,clientauth=request
https://requireclientcert.example.com:443 privileged,clientauth=require
# This is here so that we don't load the default live bookmark over
# the network in every test suite.
http://fxfeeds.mozilla.com:80
#
# These are subdomains of <ält.example.org>.
#

View File

@ -285,6 +285,8 @@ CONFIG_STATUS_DEPS := \
$(TOPSRCDIR)/.mozconfig.mk \
$(wildcard $(TOPSRCDIR)/nsprpub/configure) \
$(wildcard $(TOPSRCDIR)/config/milestone.txt) \
$(wildcard $(TOPSRCDIR)/js/src/config/milestone.txt) \
$(wildcard $(TOPSRCDIR)/browser/config/version.txt) \
$(wildcard $(addsuffix confvars.sh,$(wildcard $(TOPSRCDIR)/*/))) \
$(NULL)

View File

@ -4124,7 +4124,7 @@ nsDocument::LookupImageElement(const nsAString& aId)
if (aId.IsEmpty())
return nsnull;
nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aId);
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
return entry ? entry->GetImageIdElement() : nsnull;
}

View File

@ -854,6 +854,20 @@ nsFrameLoader::Show(PRInt32 marginWidth, PRInt32 marginHeight,
doc->SetDesignMode(NS_LITERAL_STRING("off"));
doc->SetDesignMode(NS_LITERAL_STRING("on"));
} else {
// Re-initialie the presentation for contenteditable documents
nsCOMPtr<nsIEditorDocShell> editorDocshell = do_QueryInterface(mDocShell);
if (editorDocshell) {
PRBool editable = PR_FALSE,
hasEditingSession = PR_FALSE;
editorDocshell->GetEditable(&editable);
editorDocshell->GetHasEditingSession(&hasEditingSession);
nsCOMPtr<nsIEditor> editor;
editorDocshell->GetEditor(getter_AddRefs(editor));
if (editable && hasEditingSession && editor) {
editor->PostCreate();
}
}
}
}
}

View File

@ -1450,18 +1450,6 @@ nsresult nsRange::CutContents(nsIDOMDocumentFragment** aFragment)
}
}
// XXX_kin: At this point we should be checking for the case
// XXX_kin: where we have 2 adjacent text nodes left, each
// XXX_kin: containing one of the range end points. The spec
// XXX_kin: says the 2 nodes should be merged in that case,
// XXX_kin: and to use Normalize() to do the merging, but
// XXX_kin: calling Normalize() on the common parent to accomplish
// XXX_kin: this might also normalize nodes that are outside the
// XXX_kin: range but under the common parent. Need to verify
// XXX_kin: with the range commitee members that this was the
// XXX_kin: desired behavior. For now we don't merge anything!
// XXX ajvincent Filed as https://bugzilla.mozilla.org/show_bug.cgi?id=401276
rv = CollapseRangeAfterDelete(this);
if (NS_SUCCEEDED(rv) && aFragment) {
NS_ADDREF(*aFragment = retval);

View File

@ -2320,7 +2320,6 @@ nsCanvasRenderingContext2D::SetFont(const nsAString& font)
language,
fontStyle->mFont.sizeAdjust,
fontStyle->mFont.systemFont,
fontStyle->mFont.familyNameQuirks,
printerFont,
fontStyle->mFont.featureSettings,
fontStyle->mFont.languageOverride);

View File

@ -194,7 +194,6 @@ MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
if (value && value->Type() == nsAttrValue::eString &&
!value->IsEmptyString()) {
font.mFamily.SetStringValue(value->GetStringValue(), eCSSUnit_Families);
font.mFamilyFromHTML = PR_TRUE;
}
}

View File

@ -1231,7 +1231,7 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
nsCxPusher pusher;
pusher.PushNull();
rv = newEditor->Init(domdoc, shell, GetRootNode(), mSelCon, editorFlags);
rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -403,7 +403,6 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
aData->mFontData->mFamily.GetUnit() == eCSSUnit_Null) {
aData->mFontData->mFamily.SetStringValue(value->GetStringValue(),
eCSSUnit_Families);
aData->mFontData->mFamilyFromHTML = PR_FALSE;
}
}

View File

@ -10990,8 +10990,10 @@ NS_IMETHODIMP nsNavigator::GetGeolocation(nsIDOMGeoGeolocation **_retval)
if (!mGeolocation)
return NS_ERROR_FAILURE;
if (NS_FAILED(mGeolocation->Init(contentDOMWindow)))
if (NS_FAILED(mGeolocation->Init(contentDOMWindow))) {
mGeolocation = nsnull;
return NS_ERROR_FAILURE;
}
NS_ADDREF(*_retval = mGeolocation);
return NS_OK;

View File

@ -68,8 +68,8 @@ public:
const nsAString &aCountryCode,
const nsAString &aPostalCode);
private:
~nsGeoPositionAddress();
private:
const nsString mStreetNumber;
const nsString mStreet;
const nsString mPremises;
@ -127,6 +127,10 @@ public:
nsIDOMGeoPositionAddress *aAddress,
DOMTimeStamp aTimestamp);
void SetAddress(nsIDOMGeoPositionAddress *address) {
mAddress = address;
}
private:
~nsGeoPosition();
long long mTimestamp;

View File

@ -461,16 +461,9 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
}
void
nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition, PRBool isBetter)
nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
{
// Only dispatch callbacks if this is the first position for this request, or
// if the accuracy is as good or improving.
//
// This ensures that all listeners get at least one position callback, particularly
// in the case when newly detected positions are all less accurate than the cached one.
//
// Fixes bug 596481
if (mIsFirstUpdate || isBetter) {
if (mIsFirstUpdate) {
mIsFirstUpdate = PR_FALSE;
nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
NS_DispatchToMainThread(ev);
@ -658,100 +651,13 @@ nsGeolocationService::Observe(nsISupports* aSubject,
NS_IMETHODIMP
nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
{
// here we have to determine this aSomewhere is a "better"
// position than any previously recv'ed.
PRBool isBetter = IsBetterPosition(aSomewhere);
if (isBetter) {
SetCachedPosition(aSomewhere);
}
SetCachedPosition(aSomewhere);
for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
mGeolocators[i]->Update(aSomewhere, isBetter);
mGeolocators[i]->Update(aSomewhere);
return NS_OK;
}
PRBool
nsGeolocationService::IsBetterPosition(nsIDOMGeoPosition *aSomewhere)
{
if (!aSomewhere)
return PR_FALSE;
nsRefPtr<nsGeolocationService> geoService = nsGeolocationService::GetInstance();
if (!geoService)
return PR_FALSE;
nsCOMPtr<nsIDOMGeoPosition> lastPosition = geoService->GetCachedPosition();
if (!lastPosition)
return PR_TRUE;
nsresult rv;
DOMTimeStamp oldTime;
rv = lastPosition->GetTimestamp(&oldTime);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
lastPosition->GetCoords(getter_AddRefs(coords));
if (!coords)
return PR_FALSE;
double oldAccuracy;
rv = coords->GetAccuracy(&oldAccuracy);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
double oldLat, oldLon;
rv = coords->GetLongitude(&oldLon);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
rv = coords->GetLatitude(&oldLat);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
DOMTimeStamp newTime;
rv = aSomewhere->GetTimestamp(&newTime);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
aSomewhere->GetCoords(getter_AddRefs(coords));
if (!coords)
return PR_FALSE;
double newAccuracy;
rv = coords->GetAccuracy(&newAccuracy);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
double newLat, newLon;
rv = coords->GetLongitude(&newLon);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
rv = coords->GetLatitude(&newLat);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
// check to see if there has been a large movement
// Use spherical law of cosines to calculate difference
// Not quite as correct as the Haversine but simpler and cheaper
double radsInDeg = 3.14159265 / 180.0;
double rNewLat = newLat * radsInDeg;
double rNewLon = newLon * radsInDeg;
double rOldLat = oldLat * radsInDeg;
double rOldLon = oldLon * radsInDeg;
// WGS84 equatorial radius of earth = 6378137m
double delta = acos( (sin(rNewLat) * sin(rOldLat)) + (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon)) ) * 6378137;
// The threshold is when the distance between the two positions exceeds the
// worse (larger value) of the two accuracies.
double max_accuracy = NS_MAX(oldAccuracy, newAccuracy);
if (delta > max_accuracy)
return PR_TRUE;
// check to see if the aSomewhere position is more accurate
if (oldAccuracy >= newAccuracy)
return PR_TRUE;
return PR_FALSE;
}
void
nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
{
@ -999,19 +905,19 @@ nsGeolocation::RemoveRequest(nsGeolocationRequest* aRequest)
}
void
nsGeolocation::Update(nsIDOMGeoPosition *aSomewhere, PRBool isBetter)
nsGeolocation::Update(nsIDOMGeoPosition *aSomewhere)
{
if (!WindowOwnerStillExists())
return Shutdown();
for (PRUint32 i = 0; i< mPendingCallbacks.Length(); i++) {
mPendingCallbacks[i]->Update(aSomewhere, isBetter);
mPendingCallbacks[i]->Update(aSomewhere);
}
mPendingCallbacks.Clear();
// notify everyone that is watching
for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++) {
mWatchingCallbacks[i]->Update(aSomewhere, isBetter);
mWatchingCallbacks[i]->Update(aSomewhere);
}
}

View File

@ -97,8 +97,7 @@ class nsGeolocationRequest
void Shutdown();
// Called by the geolocation device to notify that a location has changed.
// isBetter: the accuracy is as good or better than the previous position.
void Update(nsIDOMGeoPosition* aPosition, PRBool isBetter);
void Update(nsIDOMGeoPosition* aPosition);
void SendLocation(nsIDOMGeoPosition* location);
void MarkCleared();
@ -152,8 +151,6 @@ public:
void AddLocator(nsGeolocation* locator);
void RemoveLocator(nsGeolocation* locator);
PRBool IsBetterPosition(nsIDOMGeoPosition* aPosition);
void SetCachedPosition(nsIDOMGeoPosition* aPosition);
nsIDOMGeoPosition* GetCachedPosition();
@ -208,8 +205,7 @@ public:
nsresult Init(nsIDOMWindow* contentDom=nsnull);
// Called by the geolocation device to notify that a location has changed.
// isBetter: the accuracy is as good or better than the previous position.
void Update(nsIDOMGeoPosition* aPosition, PRBool isBetter);
void Update(nsIDOMGeoPosition* aPosition);
// Returns true if any of the callbacks are repeating
PRBool HasActiveCallbacks();

View File

@ -62,19 +62,23 @@ DIRS = android
endif
CPPSRCS = \
nsAccelerometer.cpp \
$(NULL)
nsAccelerometer.cpp \
$(NULL)
# On Systems that have build in geolocation providers,
# we really do not need these.
ifneq (Android,$(OS_TARGET))
EXTRA_COMPONENTS = \
NetworkGeolocationProvider.js \
NetworkGeolocationProvider.manifest \
GPSDGeolocationProvider.js \
GPSDGeolocationProvider.manifest \
$(NULL)
NetworkGeolocationProvider.js \
NetworkGeolocationProvider.manifest \
GPSDGeolocationProvider.js \
GPSDGeolocationProvider.manifest \
$(NULL)
endif
EXPORTS = \
nsAccelerometer.h \
$(NULL)
nsAccelerometer.h \
$(NULL)
include $(topsrcdir)/config/config.mk

View File

@ -493,15 +493,8 @@ nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
rv = editor->AddDocumentStateListener(mStateMaintainer);
NS_ENSURE_SUCCESS(rv, rv);
// XXXbz we really shouldn't need a presShell here!
nsCOMPtr<nsIPresShell> presShell;
rv = docShell->GetPresShell(getter_AddRefs(presShell));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(presShell);
rv = editor->Init(domDoc, presShell, nsnull /* root content */,
selCon, mEditorFlags);
rv = editor->Init(domDoc, nsnull /* root content */,
nsnull, mEditorFlags);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection> selection;

View File

@ -55,13 +55,7 @@ interface nsIEditActionListener;
interface nsIInlineSpellChecker;
interface nsITransferable;
%{C++
class nsIPresShell;
%}
[ptr] native nsIPresShellPtr(nsIPresShell);
[scriptable, uuid(972e54e1-dec3-4e3f-87ec-408a7dbcfd92)]
[scriptable, uuid(78b0bde0-ab69-428b-ab30-fcc09eead499)]
interface nsIEditor : nsISupports
{
@ -81,19 +75,15 @@ interface nsIEditor : nsISupports
/**
* Init is to tell the implementation of nsIEditor to begin its services
* @param aDoc The dom document interface being observed
* @param aPresShell TEMP: The presentation shell displaying the document.
* Once events can tell us from what pres shell
* they originated, this will no longer be
* necessary, and the editor will no longer be
* linked to a single pres shell.
* @param aRoot This is the root of the editable section of this
* document. If it is null then we get root
* from document body.
* @param aSelCon this should be used to get the selection location
* (will be null for HTML editors)
* @param aFlags A bitmask of flags for specifying the behavior
* of the editor.
*/
[noscript] void init(in nsIDOMDocument doc, in nsIPresShellPtr shell,
[noscript] void init(in nsIDOMDocument doc,
in nsIContent aRoot,
in nsISelectionController aSelCon,
in unsigned long aFlags);

View File

@ -44,6 +44,7 @@
#include "nsISelectionPrivate.h"
#include "nsISelectionController.h"
#include "nsComponentManagerUtils.h"
#include "nsIEditor.h"
// #define DEBUG_IMETXN
@ -77,7 +78,7 @@ NS_IMETHODIMP IMETextTxn::Init(nsIDOMCharacterData *aElement,
PRUint32 aReplaceLength,
nsIPrivateTextRangeList *aTextRangeList,
const nsAString &aStringToInsert,
nsWeakPtr aSelConWeak)
nsIEditor *aEditor)
{
NS_ASSERTION(aElement, "illegal value- null ptr- aElement");
NS_ASSERTION(aTextRangeList, "illegal value- null ptr - aTextRangeList");
@ -86,7 +87,7 @@ NS_IMETHODIMP IMETextTxn::Init(nsIDOMCharacterData *aElement,
mOffset = aOffset;
mReplaceLength = aReplaceLength;
mStringToInsert = aStringToInsert;
mSelConWeak = aSelConWeak;
mEditor = aEditor;
mRangeList = do_QueryInterface(aTextRangeList);
mFixed = PR_FALSE;
return NS_OK;
@ -99,7 +100,8 @@ NS_IMETHODIMP IMETextTxn::DoTransaction(void)
printf("Do IME Text element = %p replace = %d len = %d\n", mElement.get(), mReplaceLength, mStringToInsert.Length());
#endif
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
// advance caret: This requires the presentation shell to get the selection.
@ -122,7 +124,8 @@ NS_IMETHODIMP IMETextTxn::UndoTransaction(void)
printf("Undo IME Text element = %p\n", mElement.get());
#endif
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
nsresult result = mElement->DeleteData(mOffset, mStringToInsert.Length());
@ -263,7 +266,8 @@ NS_IMETHODIMP IMETextTxn::CollapseTextSelection(void)
//
// run through the text range list, if any
//
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
PRUint16 textRangeListLength,selectionStart,selectionEnd,

View File

@ -51,6 +51,7 @@
{0x9e, 0xa3, 0x0, 0x60, 0x8, 0x9f, 0xe5, 0x9b }}
class nsIEditor;
/**
@ -73,7 +74,7 @@ public:
PRUint32 aReplaceLength,
nsIPrivateTextRangeList* aTextRangeList,
const nsAString& aString,
nsWeakPtr aSelCon);
nsIEditor* aEditor);
IMETextTxn();
@ -111,8 +112,8 @@ protected:
/** the range list **/
nsCOMPtr<nsIPrivateTextRangeList> mRangeList;
/** the selection controller, which we'll need to get the selection */
nsWeakPtr mSelConWeak; // use a weak reference
/** the editor, which is used to get the selection controller */
nsIEditor *mEditor;
PRBool mFixed;
};

View File

@ -141,7 +141,6 @@ extern nsIParserService *sParserService;
nsEditor::nsEditor()
: mModCount(0)
, mFlags(0)
, mPresShellWeak(nsnull)
, mUpdateCount(0)
, mSpellcheckCheckboxState(eTriUnset)
, mPlaceHolderTxn(nsnull)
@ -159,6 +158,7 @@ nsEditor::nsEditor()
, mIsIMEComposing(PR_FALSE)
, mShouldTxnSetSelection(PR_TRUE)
, mDidPreDestroy(PR_FALSE)
, mDidPostCreate(PR_FALSE)
, mDocDirtyState(-1)
, mDocWeak(nsnull)
, mPhonetic(nsnull)
@ -224,14 +224,14 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsEditor, nsIEditor)
NS_IMETHODIMP
nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags)
nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags)
{
NS_PRECONDITION(aDoc && aPresShell, "bad arg");
if (!aDoc || !aPresShell)
NS_PRECONDITION(aDoc, "bad arg");
if (!aDoc)
return NS_ERROR_NULL_POINTER;
// First only set flags, but other stuff shouldn't be initialized now.
// Don't move this call after initializing mDocWeak and mPresShellWeak.
// Don't move this call after initializing mDocWeak.
// SetFlags() can check whether it's called during initialization or not by
// them. Note that SetFlags() will be called by PostCreate().
#ifdef DEBUG
@ -241,11 +241,19 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, nsIContent *aRoot
NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
mDocWeak = do_GetWeakReference(aDoc); // weak reference to doc
mPresShellWeak = do_GetWeakReference(aPresShell); // weak reference to pres shell
mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
// HTML editors currently don't have their own selection controller,
// so they'll pass null as aSelCon, and we'll get the selection controller
// off of the presshell.
nsCOMPtr<nsISelectionController> selCon;
if (aSelCon) {
mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller
selCon = aSelCon;
} else {
nsCOMPtr<nsIPresShell> presShell;
GetPresShell(getter_AddRefs(presShell));
selCon = do_QueryInterface(presShell);
}
NS_ASSERTION(selCon, "Selection controller should be available at this point");
//set up root element if we are passed one.
if (aRoot)
@ -259,15 +267,17 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, nsIContent *aRoot
mIMEBufferLength = 0;
/* Show the caret */
aSelCon->SetCaretReadOnly(PR_FALSE);
aSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
selCon->SetCaretReadOnly(PR_FALSE);
selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
aSelCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user
selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user
NS_POSTCONDITION(mDocWeak && mPresShellWeak, "bad state");
NS_POSTCONDITION(mDocWeak, "bad state");
// Make sure that the editor will be destroyed properly
mDidPreDestroy = PR_FALSE;
// Make sure that the ediotr will be created properly
mDidPostCreate = PR_FALSE;
return NS_OK;
}
@ -284,31 +294,38 @@ nsEditor::PostCreate()
nsresult rv = SetFlags(~mFlags);
NS_ENSURE_SUCCESS(rv, rv);
// Set up listeners
rv = CreateEventListeners();
if (NS_FAILED(rv))
{
RemoveEventListeners();
// These operations only need to happen on the first PostCreate call
if (!mDidPostCreate) {
mDidPostCreate = PR_TRUE;
return rv;
// Set up listeners
rv = CreateEventListeners();
if (NS_FAILED(rv))
{
RemoveEventListeners();
return rv;
}
rv = InstallEventListeners();
NS_ENSURE_SUCCESS(rv, rv);
// nuke the modification count, so the doc appears unmodified
// do this before we notify listeners
ResetModificationCount();
// update the UI with our state
NotifyDocumentListeners(eDocumentCreated);
NotifyDocumentListeners(eDocumentStateChanged);
}
rv = InstallEventListeners();
NS_ENSURE_SUCCESS(rv, rv);
// nuke the modification count, so the doc appears unmodified
// do this before we notify listeners
ResetModificationCount();
// update the UI with our state
NotifyDocumentListeners(eDocumentCreated);
NotifyDocumentListeners(eDocumentStateChanged);
// update nsTextStateManager and caret if we have focus
nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
if (focusedContent) {
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ASSERTION(ps, "no pres shell even though we have focus");
NS_ENSURE_TRUE(ps, NS_ERROR_UNEXPECTED);
nsPresContext* pc = ps->GetPresContext();
nsIMEStateManager::OnTextStateBlur(pc, nsnull);
@ -337,7 +354,7 @@ nsEditor::CreateEventListeners()
nsresult
nsEditor::InstallEventListeners()
{
NS_ENSURE_TRUE(mDocWeak && mPresShellWeak && mEventListener,
NS_ENSURE_TRUE(mDocWeak && mEventListener,
NS_ERROR_NOT_INITIALIZED);
// Initialize the event target.
@ -464,7 +481,7 @@ nsEditor::SetFlags(PRUint32 aFlags)
PRBool spellcheckerWasEnabled = CanEnableSpellCheck();
mFlags = aFlags;
if (!mDocWeak || !mPresShellWeak) {
if (!mDocWeak) {
// If we're initializing, we shouldn't do anything now.
// SetFlags() will be called by PostCreate(),
// we should synchronize some stuff for the flags at that time.
@ -524,10 +541,12 @@ nsEditor::GetPresShell(nsIPresShell **aPS)
{
NS_ENSURE_TRUE(aPS, NS_ERROR_NULL_POINTER);
*aPS = nsnull; // init out param
NS_PRECONDITION(mPresShellWeak, "bad state, null mPresShellWeak");
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
NS_ADDREF(*aPS = ps);
NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
*aPS = doc->GetShell();
NS_ENSURE_TRUE(*aPS, NS_ERROR_NOT_INITIALIZED);
NS_ADDREF(*aPS);
return NS_OK;
}
@ -553,8 +572,14 @@ nsEditor::GetSelectionController(nsISelectionController **aSel)
{
NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
*aSel = nsnull; // init out param
NS_PRECONDITION(mSelConWeak, "bad state, null mSelConWeak");
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
nsCOMPtr<nsISelectionController> selCon;
if (mSelConWeak) {
selCon = do_QueryReferent(mSelConWeak);
} else {
nsCOMPtr<nsIPresShell> presShell;
GetPresShell(getter_AddRefs(presShell));
selCon = do_QueryInterface(presShell);
}
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
NS_ADDREF(*aSel = selCon);
return NS_OK;
@ -574,7 +599,8 @@ nsEditor::GetSelection(nsISelection **aSelection)
{
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
*aSelection = nsnull;
nsCOMPtr<nsISelectionController> selcon = do_QueryReferent(mSelConWeak);
nsCOMPtr<nsISelectionController> selcon;
GetSelectionController(getter_AddRefs(selcon));
NS_ENSURE_TRUE(selcon, NS_ERROR_NOT_INITIALIZED);
return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); // does an addref
}
@ -1006,10 +1032,11 @@ nsEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)
// XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
NS_IMETHODIMP nsEditor::SelectAll()
{
if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
ForceCompositionEnd();
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
nsCOMPtr<nsISelectionController> selCon;
GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsISelection> selection;
nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
@ -1022,7 +1049,7 @@ NS_IMETHODIMP nsEditor::SelectAll()
NS_IMETHODIMP nsEditor::BeginningOfDocument()
{
if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
// get the selection
nsCOMPtr<nsISelection> selection;
@ -1067,7 +1094,7 @@ NS_IMETHODIMP nsEditor::BeginningOfDocument()
NS_IMETHODIMP
nsEditor::EndOfDocument()
{
if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
nsresult res;
// get selection
@ -1118,39 +1145,23 @@ nsEditor::GetDocumentModified(PRBool *outDocModified)
NS_IMETHODIMP
nsEditor::GetDocumentCharacterSet(nsACString &characterSet)
{
nsCOMPtr<nsIPresShell> presShell;
nsresult rv = GetPresShell(getter_AddRefs(presShell));
if (NS_SUCCEEDED(rv))
{
nsIDocument *doc = presShell->GetDocument();
if (doc) {
characterSet = doc->GetDocumentCharacterSet();
return NS_OK;
}
rv = NS_ERROR_NULL_POINTER;
}
return rv;
nsresult rv = NS_OK;
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
characterSet = doc->GetDocumentCharacterSet();
return NS_OK;
}
NS_IMETHODIMP
nsEditor::SetDocumentCharacterSet(const nsACString& characterSet)
{
nsCOMPtr<nsIPresShell> presShell;
nsresult rv = GetPresShell(getter_AddRefs(presShell));
if (NS_SUCCEEDED(rv))
{
nsIDocument *doc = presShell->GetDocument();
if (doc) {
doc->SetDocumentCharacterSet(characterSet);
return NS_OK;
}
rv = NS_ERROR_NULL_POINTER;
}
nsresult rv = NS_OK;
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
return rv;
doc->SetDocumentCharacterSet(characterSet);
return NS_OK;
}
NS_IMETHODIMP
@ -2845,7 +2856,8 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode,
}
}
// handle selection
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
if (ps)
ps->FlushPendingNotifications(Flush_Frames);
@ -3973,7 +3985,8 @@ nsEditor::IsPreformatted(nsIDOMNode *aNode, PRBool *aResult)
NS_ENSURE_TRUE(aResult && content, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
// Look at the node (and its parent if it's not an element), and grab its style context
@ -4622,7 +4635,7 @@ nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
nsRefPtr<IMETextTxn> txn = new IMETextTxn();
nsresult rv = txn->Init(mIMETextNode, mIMETextOffset, mIMEBufferLength,
mIMETextRangeList, aStringToInsert, mSelConWeak);
mIMETextRangeList, aStringToInsert, this);
if (NS_SUCCEEDED(rv))
{
txn.forget(aTxn);
@ -4674,7 +4687,8 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction,
*aTxn = nsnull;
nsRefPtr<EditAggregateTxn> aggTxn;
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
nsCOMPtr<nsISelectionController> selCon;
GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsISelection> selection;
nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
@ -4756,7 +4770,7 @@ nsEditor::CreateTxnForDeleteCharacter(nsIDOMCharacterData *aData,
}
} else {
segOffset = aOffset - 1;
if (segOffset > 1 &&
if (segOffset > 0 &&
NS_IS_LOW_SURROGATE(data[segOffset]) &&
NS_IS_HIGH_SURROGATE(data[segOffset-1])) {
++segLength;

View File

@ -725,7 +725,6 @@ protected:
PRUint32 mModCount; // number of modifications (for undo/redo stack)
PRUint32 mFlags; // behavior flags. See nsIPlaintextEditor.idl for the flags we use.
nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController
PRInt32 mUpdateCount;
nsIViewManager::UpdateViewBatch mBatch;
@ -760,6 +759,7 @@ protected:
PRPackedBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
PRPackedBool mDidPreDestroy; // whether PreDestroy has been called
PRPackedBool mDidPostCreate; // whether PostCreate has been called
// various listeners
nsCOMArray<nsIEditActionListener> mActionListeners; // listens to all low level actions on the doc
nsCOMArray<nsIEditorObserver> mEditorObservers; // just notify once per high level change

View File

@ -93,7 +93,7 @@ private:
};
nsEditorEventListener::nsEditorEventListener() :
mEditor(nsnull), mCaretDrawn(PR_FALSE), mCommitText(PR_FALSE),
mEditor(nsnull), mCommitText(PR_FALSE),
mInTransaction(PR_FALSE)
{
}
@ -165,8 +165,9 @@ nsEditorEventListener::InstallToEditor()
NS_EVENT_FLAG_BUBBLE, sysGroup);
NS_ENSURE_SUCCESS(rv, rv);
rv = piTarget->AddEventListenerByIID(static_cast<nsIDOMMouseListener*>(this),
NS_GET_IID(nsIDOMMouseListener));
rv = elmP->AddEventListenerByIID(static_cast<nsIDOMMouseListener*>(this),
NS_GET_IID(nsIDOMMouseListener),
NS_EVENT_FLAG_CAPTURE);
NS_ENSURE_SUCCESS(rv, rv);
// Focus event doesn't bubble so adding the listener to capturing phase.
@ -236,8 +237,9 @@ nsEditorEventListener::UninstallFromEditor()
NS_LITERAL_STRING("drop"),
NS_EVENT_FLAG_BUBBLE, sysGroup);
piTarget->RemoveEventListenerByIID(static_cast<nsIDOMMouseListener*>(this),
NS_GET_IID(nsIDOMMouseListener));
elmP->RemoveEventListenerByIID(static_cast<nsIDOMMouseListener*>(this),
NS_GET_IID(nsIDOMMouseListener),
NS_EVENT_FLAG_CAPTURE);
elmP->RemoveEventListenerByIID(static_cast<nsIDOMFocusListener*>(this),
NS_GET_IID(nsIDOMFocusListener),
@ -559,7 +561,6 @@ nsEditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent)
mCaret->Init(presShell);
mCaret->SetCaretReadOnly(PR_TRUE);
}
mCaretDrawn = PR_FALSE;
}
presShell->SetCaret(mCaret);
@ -598,52 +599,30 @@ nsEditorEventListener::DragOver(nsIDOMDragEvent* aDragEvent)
NS_ENSURE_SUCCESS(rv, rv);
// to avoid flicker, we could track the node and offset to see if we moved
if (mCaretDrawn)
if (mCaret)
mCaret->EraseCaret();
//mCaret->SetCaretVisible(PR_TRUE); // make sure it's visible
mCaret->DrawAtPosition(parent, offset);
mCaretDrawn = PR_TRUE;
}
}
else
{
if (mCaret && mCaretDrawn)
if (mCaret)
{
mCaret->EraseCaret();
mCaretDrawn = PR_FALSE;
}
}
return NS_OK;
}
nsresult
nsEditorEventListener::DragExit(nsIDOMDragEvent* aDragEvent)
{
if (mCaret && mCaretDrawn)
{
mCaret->EraseCaret();
mCaretDrawn = PR_FALSE;
}
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
if (presShell)
presShell->RestoreCaret();
return NS_OK;
}
nsresult
nsEditorEventListener::Drop(nsIDOMDragEvent* aMouseEvent)
void
nsEditorEventListener::CleanupDragDropCaret()
{
if (mCaret)
{
if (mCaretDrawn)
{
mCaret->EraseCaret();
mCaretDrawn = PR_FALSE;
}
mCaret->EraseCaret();
mCaret->SetCaretVisible(PR_FALSE); // hide it, so that it turns off its timer
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
@ -651,7 +630,24 @@ nsEditorEventListener::Drop(nsIDOMDragEvent* aMouseEvent)
{
presShell->RestoreCaret();
}
mCaret->Terminate();
mCaret = nsnull;
}
}
nsresult
nsEditorEventListener::DragExit(nsIDOMDragEvent* aDragEvent)
{
CleanupDragDropCaret();
return NS_OK;
}
nsresult
nsEditorEventListener::Drop(nsIDOMDragEvent* aMouseEvent)
{
CleanupDragDropCaret();
nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent = do_QueryInterface(aMouseEvent);
if (nsuiEvent) {

View File

@ -105,12 +105,12 @@ protected:
nsresult DragExit(nsIDOMDragEvent* aDragEvent);
nsresult Drop(nsIDOMDragEvent* aDragEvent);
nsresult DragGesture(nsIDOMDragEvent* aDragEvent);
void CleanupDragDropCaret();
already_AddRefed<nsIPresShell> GetPresShell();
protected:
nsEditor* mEditor; // weak
nsRefPtr<nsCaret> mCaret;
PRPackedBool mCaretDrawn;
PRPackedBool mCommitText;
PRPackedBool mInTransaction;
};

View File

@ -311,7 +311,8 @@ nsHTMLEditor::HideGrabber()
NS_ENSURE_TRUE(mGrabber, NS_ERROR_NULL_POINTER);
// get the presshell's document observer interface.
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
// We allow the pres shell to be null; when it is, we presume there
// are no document observers to notify, but we still want to
// UnbindFromTree.
@ -427,7 +428,8 @@ nsresult
nsHTMLEditor::EndMoving()
{
if (mPositioningShadow) {
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIDOMNode> parentNode;

View File

@ -156,7 +156,8 @@ nsHTMLEditor::CreateAnonymousElement(const nsAString & aTag, nsIDOMNode * aPare
NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
// Get the pres shell
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
// Create a new node through the element factory

View File

@ -204,9 +204,10 @@ nsHTMLEditor::~nsHTMLEditor()
RemoveOverrideStyleSheet(mStyleSheetURLs[0]);
}
if (mLinkHandler && mPresShellWeak)
if (mLinkHandler && mDocWeak)
{
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
if (ps && ps->GetPresContext())
{
@ -295,12 +296,13 @@ NS_INTERFACE_MAP_END_INHERITING(nsPlaintextEditor)
NS_IMETHODIMP
nsHTMLEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell,
nsIContent *aRoot, nsISelectionController *aSelCon,
nsHTMLEditor::Init(nsIDOMDocument *aDoc,
nsIContent *aRoot,
nsISelectionController *aSelCon,
PRUint32 aFlags)
{
NS_PRECONDITION(aDoc && aPresShell, "bad arg");
NS_ENSURE_TRUE(aDoc && aPresShell, NS_ERROR_NULL_POINTER);
NS_PRECONDITION(aDoc && !aSelCon, "bad arg");
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
nsresult result = NS_OK, rulesRes = NS_OK;
@ -317,7 +319,7 @@ nsHTMLEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell,
nsAutoEditInitRulesTrigger rulesTrigger(static_cast<nsPlaintextEditor*>(this), rulesRes);
// Init the plaintext editor
result = nsPlaintextEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags);
result = nsPlaintextEditor::Init(aDoc, aRoot, nsnull, aFlags);
if (NS_FAILED(result)) { return result; }
// Init mutation observer
@ -337,7 +339,10 @@ nsHTMLEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell,
mHTMLCSSUtils->Init(this);
// disable links
nsPresContext *context = aPresShell->GetPresContext();
nsCOMPtr<nsIPresShell> presShell;
GetPresShell(getter_AddRefs(presShell));
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
nsPresContext *context = presShell->GetPresContext();
NS_ENSURE_TRUE(context, NS_ERROR_NULL_POINTER);
if (!IsPlaintextEditor() && !IsInteractionAllowed()) {
mLinkHandler = context->GetLinkHandler();
@ -487,7 +492,7 @@ nsHTMLEditor::CreateEventListeners()
nsresult
nsHTMLEditor::InstallEventListeners()
{
NS_ENSURE_TRUE(mDocWeak && mPresShellWeak && mEventListener,
NS_ENSURE_TRUE(mDocWeak && mEventListener,
NS_ERROR_NOT_INITIALIZED);
// NOTE: nsHTMLEditor doesn't need to initialize mEventTarget here because
@ -571,7 +576,7 @@ nsHTMLEditor::InitRules()
NS_IMETHODIMP
nsHTMLEditor::BeginningOfDocument()
{
if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
// get the selection
nsCOMPtr<nsISelection> selection;
@ -3543,8 +3548,9 @@ nsHTMLEditor::ReplaceStyleSheet(const nsAString& aURL)
}
// Make sure the pres shell doesn't disappear during the load.
NS_ENSURE_TRUE(mPresShellWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIURI> uaURI;
@ -3588,7 +3594,8 @@ nsHTMLEditor::AddOverrideStyleSheet(const nsAString& aURL)
return NS_OK;
// Make sure the pres shell doesn't disappear during the load.
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIURI> uaURI;
@ -3651,8 +3658,9 @@ nsHTMLEditor::RemoveOverrideStyleSheet(const nsAString &aURL)
NS_ENSURE_TRUE(sheet, NS_OK); /// Don't fail if sheet not found
NS_ENSURE_TRUE(mPresShellWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
ps->RemoveOverrideStyleSheet(sheet);
@ -4248,7 +4256,8 @@ nsHTMLEditor::SelectAll()
ForceCompositionEnd();
nsresult rv;
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak, &rv);
nsCOMPtr<nsISelectionController> selCon;
rv = GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection> selection;
@ -4273,7 +4282,8 @@ nsHTMLEditor::SelectAll()
return selection->SelectAllChildren(mRootElement);
}
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
nsIContent *rootContent = anchorContent->GetSelectionRootContent(ps);
NS_ENSURE_TRUE(rootContent, NS_ERROR_UNEXPECTED);
@ -5680,8 +5690,9 @@ nsHTMLEditor::GetElementOrigin(nsIDOMElement * aElement, PRInt32 & aX, PRInt32 &
aX = 0;
aY = 0;
NS_ENSURE_TRUE(mPresShellWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);

View File

@ -329,7 +329,7 @@ public:
nsresult EndUpdateViewBatch();
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD PreDestroy(PRBool aDestroyingFrames);
/** Internal, static version */

View File

@ -124,7 +124,8 @@ nsHTMLEditor::HideInlineTableEditingUI()
RemoveMouseClickListener(mAddRowAfterButton);
// get the presshell's document observer interface.
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
// We allow the pres shell to be null; when it is, we presume there
// are no document observers to notify, but we still want to
// UnbindFromTree.

View File

@ -432,7 +432,8 @@ nsHTMLEditor::HideResizers(void)
NS_ENSURE_TRUE(mResizedObject, NS_OK);
// get the presshell's document observer interface.
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
// We allow the pres shell to be null; when it is, we presume there
// are no document observers to notify, but we still want to
// UnbindFromTree.

View File

@ -2642,8 +2642,9 @@ nsHTMLEditor::GetCellIndexes(nsIDOMElement *aCell,
return NS_ERROR_FAILURE;
}
NS_ENSURE_TRUE(mPresShellWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aCell) );
@ -2662,8 +2663,9 @@ nsHTMLEditor::GetTableLayoutObject(nsIDOMElement* aTable, nsITableLayout **table
{
*tableLayoutObject=nsnull;
NS_ENSURE_TRUE(aTable, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(mPresShellWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aTable) );

View File

@ -72,6 +72,7 @@ _TEST_FILES = \
test_bug620906.html \
test_bug622371.html \
test_bug629845.html \
test_bug640321.html \
test_CF_HTML_clipboard.html \
test_contenteditable_focus.html \
test_contenteditable_text_input_handling.html \
@ -92,6 +93,7 @@ _CHROME_TEST_FILES = \
test_bug490879.xul \
test_bug607584.xul \
test_bug616590.xul \
test_bug635636.html \
green.png \
$(NULL)

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,58 @@
README FOR BROWSERSCOPE
-----------------------
Hey there - thanks for downloading the code. This file has instructions
for getting setup so that you can run the codebase locally.
This project is built on Google App Engine using the
Django web application framework and written in Python.
To get started, you'll need to first download the App Engine SDK at:
http://code.google.com/appengine/downloads.html
For local development, just startup the server:
./pathto/google_appengine/dev_appserver.py --port=8080 browserscope
You should then be able to access the local application at:
http://localhost:8080/
Note: the first time you hit the homepage it may take a little
while - that's because it's trying to read out median times for all
of the tests from a nonexistent datastore and write to memcache.
Just be a lil patient.
You can run the unit tests at:
http://localhost:8080/test
CONTRIBUTING
------------------
Most likely you are interested in adding new tests or creating
a new test category. If you are interested in adding tests to an existing
"category" you may want to get in touch with the maintainer for that
branch of the tree. We are really looking forward to receiving your
code in patch format. Currently the category maintainers are:
Network: Steve Souders <souders@gmail.com>
Reflow: Lindsey Simon <elsigh@gmail.com>
Security: Adam Barth <adam@adambarth.com> and Collin Jackson <collin@collinjackson.com>
To create a completely new test category:
* Copy one of the existing directories in categories/
* Edit your test_set.py, handlers.py
* Add your files in templates/ and static/
* Update urls.py and settings.CATEGORIES
* Follow the examples of other tests re:
* beaconing using/testdriver_base
* your GetScoreAndDisplayValue method
* your GetRowScoreAndDisplayValue method
References:
* App Engine Docs - http://code.google.com/appengine/docs/python/overview.html
* App Engine Group - http://groups.google.com/group/google-appengine
* Python Docs - http://www.python.org/doc/
* Django - http://www.djangoproject.com/

View File

@ -0,0 +1,23 @@
The BrowserScope project provides a set of cross-browser HTML editor tests,
which we import in our test suite in order to run them as part of our
continuous integration system.
We pull tests occasionally from their Subversion repository using the pull
script which can be found in this directory. We also record the revision ID
which we've used in the current_revision file inside this directory.
Using the pull script is quite easy, just switch to this directory, and say:
sh update_from_upstream
There are tests which we're currently failing on, and there will probably be
more of those in the future. We should maintain a list of the failing tests
manually in currentStatus.js (which can also be found in this directory), to
make sure that the suite passes entirely, with failing tests marked as todo
items.
The current status of the test suite needs to be updated whenever an editor
bug gets fixed, which makes us pass one of the tests. When that happens,
you should set the UPDATE_TEST_RESULTS constant to true in test_richtext2.html,
run the test suite, paste the result JSON string in a JSON beautifier (such
as http://jsbeautifier.org/), and use the result to update currentStatus.js.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
#!/usr/bin/python2.5
#
# Copyright 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the 'License')
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Common constants"""
__author__ = 'rolandsteiner@google.com (Roland Steiner)'
CATEGORY = 'richtext2'
TEST_ID_PREFIX = 'RTE2'
CLASSES = ['Finalized', 'RFC', 'Proposed']

View File

@ -0,0 +1,107 @@
#!/usr/bin/python2.5
#
# Copyright 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the 'License')
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Handlers for New Rich Text Tests"""
__author__ = 'rolandsteiner@google.com (Roland Steiner)'
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.api import memcache
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
import django
from django import http
from django import shortcuts
from django.template import add_to_builtins
add_to_builtins('base.custom_filters')
# Shared stuff
from categories import all_test_sets
from base import decorators
from base import util
# common to the RichText2 suite
from categories.richtext2 import common
# tests
from categories.richtext2.tests.apply import APPLY_TESTS
from categories.richtext2.tests.applyCSS import APPLY_TESTS_CSS
from categories.richtext2.tests.change import CHANGE_TESTS
from categories.richtext2.tests.changeCSS import CHANGE_TESTS_CSS
from categories.richtext2.tests.delete import DELETE_TESTS
from categories.richtext2.tests.forwarddelete import FORWARDDELETE_TESTS
from categories.richtext2.tests.insert import INSERT_TESTS
from categories.richtext2.tests.selection import SELECTION_TESTS
from categories.richtext2.tests.unapply import UNAPPLY_TESTS
from categories.richtext2.tests.unapplyCSS import UNAPPLY_TESTS_CSS
from categories.richtext2.tests.querySupported import QUERYSUPPORTED_TESTS
from categories.richtext2.tests.queryEnabled import QUERYENABLED_TESTS
from categories.richtext2.tests.queryIndeterm import QUERYINDETERM_TESTS
from categories.richtext2.tests.queryState import QUERYSTATE_TESTS, QUERYSTATE_TESTS_CSS
from categories.richtext2.tests.queryValue import QUERYVALUE_TESTS, QUERYVALUE_TESTS_CSS
def About(request):
"""About page."""
overview = """These tests cover browers' implementations of
<a href="http://blog.whatwg.org/the-road-to-html-5-contenteditable">contenteditable</a>
for basic rich text formatting commands. Most browser implementations do very
well at editing the HTML which is generated by their own execCommands. But a
big problem happens when developers try to make cross-browser web
applications using contenteditable - most browsers are not able to correctly
change formatting generated by other browsers. On top of that, most browsers
allow users to to paste arbitrary HTML from other webpages into a
contenteditable region, which is even harder for browsers to properly
format. These tests check how well the execCommand, queryCommandState,
and queryCommandValue functions work with different types of HTML."""
return util.About(request, common.CATEGORY, category_title='Rich Text',
overview=overview, show_hidden=False)
def RunRichText2Tests(request):
params = {
'classes': common.CLASSES,
'commonIDPrefix': common.TEST_ID_PREFIX,
'strict': False,
'suites': [
SELECTION_TESTS,
APPLY_TESTS,
APPLY_TESTS_CSS,
CHANGE_TESTS,
CHANGE_TESTS_CSS,
UNAPPLY_TESTS,
UNAPPLY_TESTS_CSS,
DELETE_TESTS,
FORWARDDELETE_TESTS,
INSERT_TESTS,
QUERYSUPPORTED_TESTS,
QUERYENABLED_TESTS,
QUERYINDETERM_TESTS,
QUERYSTATE_TESTS,
QUERYSTATE_TESTS_CSS,
QUERYVALUE_TESTS,
QUERYVALUE_TESTS_CSS
]
}
return shortcuts.render_to_response('%s/templates/richtext2.html' % common.CATEGORY, params)

View File

@ -0,0 +1,116 @@
.framed {
vertical-align: top;
margin: 8px;
border: 1px solid black;
}
.legend {
padding: 12px;
background-color: #f8f8ff;
}
.legendHdr {
font-size: large;
text-decoration: underline;
}
table.legend {
display: inline-table;
}
.suite-thead {
text-align: left;
}
.lo {
background-color: #dddddd;
}
.hi {
background-color: #eeeeee;
}
.lo .grey {
background-color: #dddddd;
}
.lo .na {
background-color: #dddddd;
}
.lo .pass {
background-color: #d4ffc0;
}
.lo .canary {
background-color: #ffcccc;
}
.lo .fail {
background-color: #ffcccc;
}
.lo .accept {
background-color: #ffffc0;
}
.lo .exception {
background-color: #f0d0f4;
}
.lo .unsupported {
background-color: #f0d0f4;
}
.hi .grey {
background-color: #eeeeee;
}
.hi .na {
background-color: #eeeeee;
}
.hi .pass {
background-color: #e0ffdc;
}
.hi .canary {
background-color: #ffd8d8;
}
.hi .fail {
background-color: #ffd8d8;
}
.hi .accept {
background-color: #ffffd8;
}
.hi .exception {
background-color: #f4dcf8;
}
.hi .unsupported {
background-color: #f4dcf8;
}
.sel {
color: blue;
}
.txt {
padding: 1px;
margin: 1px;
border: 1px solid #b0b0b0;
}
.idLabel {
font-size: small;
}
.fade {
color: grey;
}
.accexp {
color: #606070;
}
.comment {
color: grey;
}
.score {
color: #666666;
}
.fatalerror {
color: red;
font-size: large;
font-weight: bold;
}

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="editable.css" type="text/css">
</head>
<body contentEditable="true">
</body>
</html>

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="editable.css" type="text/css">
<script>
function setDesignMode() {
window.document.designMode = "On";
}
</script>
</head>
<body onload="setDesignMode()">
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="editable.css" type="text/css">
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,66 @@
.b, myb {
font-weight: bold;
}
.i, myi {
font-style: italic;
}
.s, mys {
text-decoration: line-through;
}
.u, myu {
text-decoration: underline;
}
.sub, mysub {
vertical-align: sub;
}
.sup, mysup {
vertical-align: super;
}
.jc, myjc {
text-align: center;
}
.jf, myjf {
text-align: justify;
}
.jl, myjl {
text-align: left;
}
.jr, myjr {
text-align: right;
}
.red, myred {
color: red;
}
.bcred, mybcred {
background-color: red;
}
.large, mylarge {
font-size: large;
}
.fs18px, myfs18px {
font-size: 18px;
}
.courier, mycourier {
font-family: courier;
}
gen::before {
content: "[BEFORE]";
}
gen::after {
content: "[AFTER]";
}

View File

@ -0,0 +1,436 @@
/**
* @fileoverview
* Canonicalization functions used in the RTE test suite.
*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the 'License')
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @version 0.1
* @author rolandsteiner@google.com
*/
/**
* Canonicalize HTML entities to their actual character
*
* @param str {String} the HTML string to be canonicalized
* @return {String} the canonicalized string
*/
function canonicalizeEntities(str) {
// TODO(rolandsteiner): this function is very much not optimized, but that shouldn't
// theoretically matter too much - look into it at some point.
var match;
while (match = str.match(/&#x([0-9A-F]+);/i)) {
str = str.replace('&#x' + match[1] + ';', String.fromCharCode(parseInt(match[1], 16)));
}
while (match = str.match(/&#([0-9]+);/)) {
str = str.replace('&#' + match[1] + ';', String.fromCharCode(Number(match[1])));
}
return str;
}
/**
* Canonicalize the contents of the HTML 'style' attribute.
* I.e. sorts the CSS attributes alphabetically and canonicalizes the values
* CSS attributes where necessary.
*
* If this would return an empty string, return null instead to suppress the
* whole 'style' attribute.
*
* Avoid tests that contain {, } or : within CSS values!
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* FIXME: does not canonicalize the contents of compound attributes
* (e.g., 'border').
*
* @param str {String} contents of the 'style' attribute
* @param emitFlags {Object} flags used for this output
* @return {String/null} canonicalized string, null instead of the empty string
*/
function canonicalizeStyle(str, emitFlags) {
// Remove any enclosing curly brackets
str = str.replace(/ ?[\{\}] ?/g, '');
var attributes = str.split(';');
var count = attributes.length;
var resultArr = [];
for (var a = 0; a < count; ++a) {
// Retrieve "name: value" pair
// Note: may expectedly fail if the last pair was terminated with ';'
var avPair = attributes[a].match(/ ?([^ :]+) ?: ?(.+)/);
if (!avPair)
continue;
var name = avPair[1];
var value = avPair[2].replace(/ $/, ''); // Remove any trailing space.
switch (name) {
case 'color':
case 'background-color':
case 'border-color':
if (emitFlags.canonicalizeUnits) {
resultArr.push(name + ': #' + new Color(value).toHexString());
} else {
resultArr.push(name + ': ' + value);
}
break;
case 'font-family':
if (emitFlags.canonicalizeUnits) {
resultArr.push(name + ': ' + new FontName(value).toString());
} else {
resultArr.push(name + ': ' + value);
}
break;
case 'font-size':
if (emitFlags.canonicalizeUnits) {
resultArr.push(name + ': ' + new FontSize(value).toString());
} else {
resultArr.push(name + ': ' + value);
}
break;
default:
resultArr.push(name + ': ' + value);
}
}
// Sort by name, assuming no duplicate CSS attribute names.
resultArr.sort();
return resultArr.join('; ') || null;
}
/**
* Canonicalize a single attribute value.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param elemName {String} the name of the element
* @param attrName {String} the name of the attribute
* @param attrValue {String} the value of the attribute
* @param emitFlags {Object} flags used for this output
* @return {String/null} the canonicalized value, or null if the attribute should be skipped.
*/
function canonicalizeSingleAttribute(elemName, attrName, attrValue, emitFlags) {
// We emit attributes as name="value", so change any contained apostrophes
// to quote marks.
attrValue = attrValue.replace(/\x22/, '\x27');
switch (attrName) {
case 'class':
return emitFlags.emitClass ? attrValue : null;
case 'id':
if (!emitFlags.emitID) {
return null;
}
if (attrValue && attrValue.substr(0, 7) == 'editor-') {
return null;
}
return attrValue;
// Remove empty style attributes, canonicalize the contents otherwise,
// provided the test cares for styles.
case 'style':
return (emitFlags.emitStyle && attrValue)
? canonicalizeStyle(attrValue, emitFlags)
: null;
// Never output onload handlers as they are set by the test environment.
case 'onload':
return null;
// Canonicalize colors.
case 'bgcolor':
case 'color':
if (!attrValue) {
return null;
}
return emitFlags.canonicalizeUnits ? new Color(attrValue).toString() : attrValue;
// Canonicalize font names.
case 'face':
return emitFlags.canonicalizeUnits ? new FontName(attrValue).toString() : attrValue;
// Canonicalize font sizes (leave other 'size' attributes as-is).
case 'size':
if (!attrValue) {
return null;
}
switch (elemName) {
case 'basefont':
case 'font':
return emitFlags.canonicalizeUnits ? new FontSize(attrValue).toString() : attrValue;
}
return attrValue;
// Remove spans with value 1. Retain spans with other values, even if
// empty or with a value 0, since those indicate a flawed implementation.
case 'colspan':
case 'rowspan':
case 'span':
return (attrValue == '1' || attrValue === '') ? null : attrValue;
// Boolean attributes: presence equals true. If present, the value must be
// the empty string or the attribute's canonical name.
// (http://www.whatwg.org/specs/web-apps/current-work/#boolean-attributes)
// Below we only normalize empty string to the canonical name for
// comparison purposes. All other values are not touched and will therefore
// in all likelihood result in a failed test (even if they may be accepted
// by the UA).
case 'async':
case 'autofocus':
case 'checked':
case 'compact':
case 'declare':
case 'defer':
case 'disabled':
case 'formnovalidate':
case 'frameborder':
case 'ismap':
case 'loop':
case 'multiple':
case 'nohref':
case 'nosize':
case 'noshade':
case 'novalidate':
case 'nowrap':
case 'open':
case 'readonly':
case 'required':
case 'reversed':
case 'seamless':
case 'selected':
return attrValue ? attrValue : attrName;
default:
return attrValue;
}
}
/**
* Canonicalize the contents of an element tag.
*
* I.e. sorts the attributes alphabetically and canonicalizes their
* values where necessary. Also removes attributes we're not interested in.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param str {String} the contens of the element tag, excluding < and >.
* @param emitFlags {Object} flags used for this output
* @return {String} the canonicalized contents.
*/
function canonicalizeElementTag(str, emitFlags) {
// FIXME: lowercase only if emitFlags.lowercase is set
str = str.toLowerCase();
var pos = str.search(' ');
// element name only
if (pos == -1) {
return str;
}
var elemName = str.substr(0, pos);
str = str.substr(pos + 1);
// Even if emitFlags.emitAttrs is not set, we must iterate over the
// attributes to catch the special selection attribute and/or selection
// markers. :(
// Iterate over attributes, add them to an array, canonicalize their
// contents, and finally output the (remaining) attributes in sorted order.
// Note: We can't do a simple split on space here, because the value of,
// e.g., 'style' attributes may also contain spaces.
var attrs = [];
var selStartInTag = false;
var selEndInTag = false;
while (str) {
var attrName;
var attrValue = '';
pos = str.search(/[ =]/);
if (pos >= 0) {
attrName = str.substr(0, pos);
if (str.charAt(pos) == ' ') {
++pos;
}
if (str.charAt(pos) == '=') {
++pos;
if (str.charAt(pos) == ' ') {
++pos;
}
str = str.substr(pos);
switch (str.charAt(0)) {
case '"':
case "'":
pos = str.indexOf(str.charAt(0), 1);
pos = (pos < 0) ? str.length : pos;
attrValue = str.substring(1, pos);
++pos;
break;
default:
pos = str.indexOf(' ', 0);
pos = (pos < 0) ? str.length : pos;
attrValue = (pos == -1) ? str : str.substr(0, pos);
break;
}
attrValue = attrValue.replace(/^ /, '');
attrValue = attrValue.replace(/ $/, '');
}
} else {
attrName = str;
}
str = (pos == -1 || pos >= str.length) ? '' : str.substr(pos + 1);
// Remove special selection attributes.
switch (attrName) {
case ATTRNAME_SEL_START:
selStartInTag = true;
continue;
case ATTRNAME_SEL_END:
selEndInTag = true;
continue;
}
switch (attrName) {
case '':
case 'onload':
case 'xmlns':
break;
default:
if (!emitFlags.emitAttrs) {
break;
}
// >>> fall through >>>
case 'contenteditable':
attrValue = canonicalizeEntities(attrValue);
attrValue = canonicalizeSingleAttribute(elemName, attrName, attrValue, emitFlags);
if (attrValue !== null) {
attrs.push(attrName + '="' + attrValue + '"');
}
}
}
var result = elemName;
// Sort alphabetically (on full string rather than just attribute value for
// simplicity. Also, attribute names will differ when encountering the '=').
if (attrs.length > 0) {
attrs.sort();
result += ' ' + attrs.join(' ');
}
// Add intra-tag selection marker(s) or attribute(s), if any, at the end.
if (selStartInTag && selEndInTag) {
result += ' |';
} else if (selStartInTag) {
result += ' {';
} else if (selEndInTag) {
result += ' }';
}
return result;
}
/**
* Canonicalize elements and attributes to facilitate comparison to the
* expectation string: sort attributes, canonicalize values and remove chaff.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param str {String} the HTML string to be canonicalized
* @param emitFlags {Object} flags used for this output
* @return {String} the canonicalized string
*/
function canonicalizeElementsAndAttributes(str, emitFlags) {
var tagStart = str.indexOf('<');
var tagEnd = 0;
var result = '';
while (tagStart >= 0) {
++tagStart;
if (str.charAt(tagStart) == '/') {
++tagStart;
}
result = result + canonicalizeEntities(str.substring(tagEnd, tagStart));
tagEnd = str.indexOf('>', tagStart);
if (tagEnd < 0) {
tagEnd = str.length - 1;
}
if (str.charAt(tagEnd - 1) == '/') {
--tagEnd;
}
var elemStr = str.substring(tagStart, tagEnd);
elemStr = canonicalizeElementTag(elemStr, emitFlags);
result = result + elemStr;
tagStart = str.indexOf('<', tagEnd);
}
return result + canonicalizeEntities(str.substring(tagEnd));
}
/**
* Canonicalize an innerHTML string to uniform single whitespaces.
*
* FIXME: running this prevents testing for pre-formatted content
* and the CSS 'white-space' attribute.
*
* @param str {String} the HTML string to be canonicalized
* @return {String} the canonicalized string
*/
function canonicalizeSpaces(str) {
// Collapse sequential whitespace.
str = str.replace(/\s+/g, ' ');
// Remove spaces immediately inside angle brackets <, >, </ and />.
// While doing this also canonicalize <.../> to <...>.
str = str.replace(/\< ?/g, '<');
str = str.replace(/\<\/ ?/g, '</');
str = str.replace(/ ?\/?\>/g, '>');
return str;
}
/**
* Canonicalize an innerHTML string to uniform single whitespaces.
* Also remove comments to retain only embedded selection markers, and
* remove </br> and </hr> if present.
*
* FIXME: running this prevents testing for pre-formatted content
* and the CSS 'white-space' attribute.
*
* @param str {String} the HTML string to be canonicalized
* @return {String} the canonicalized string
*/
function initialCanonicalizationOf(str) {
str = canonicalizeSpaces(str);
str = str.replace(/ ?<!-- ?/g, '');
str = str.replace(/ ?--> ?/g, '');
str = str.replace(/<\/[bh]r>/g, '');
return str;
}

View File

@ -0,0 +1,489 @@
/**
* @fileoverview
* Comparison functions used in the RTE test suite.
*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the 'License')
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @version 0.1
* @author rolandsteiner@google.com
*/
/**
* constants used only in the compare functions.
*/
var RESULT_DIFF = 0; // actual result doesn't match expectation
var RESULT_SEL = 1; // actual result matches expectation in HTML only
var RESULT_EQUAL = 2; // actual result matches expectation in both HTML and selection
/**
* Gets the test expectations as an array from the passed-in field.
*
* @param {Array|String} the test expectation(s) as string or array.
* @return {Array} test expectations as an array.
*/
function getExpectationArray(expected) {
if (expected === undefined) {
return [];
}
if (expected === null) {
return [null];
}
switch (typeof expected) {
case 'string':
case 'boolean':
case 'number':
return [expected];
}
// Assume it's already an array.
return expected;
}
/**
* Compare a test result to a single expectation string.
*
* FIXME: add support for optional elements/attributes.
*
* @param expected {String} the already canonicalized (with the exception of selection marks) expectation string
* @param actual {String} the already canonicalized (with the exception of selection marks) actual result
* @return {Integer} one of the RESULT_... return values
* @see variables.js for return values
*/
function compareHTMLToSingleExpectation(expected, actual) {
// If the test checks the selection, then the actual string must match the
// expectation exactly.
if (expected == actual) {
return RESULT_EQUAL;
}
// Remove selection markers and see if strings match then.
expected = expected.replace(/ [{}\|]>/g, '>'); // intra-tag
expected = expected.replace(/[\[\]\^{}\|]/g, ''); // outside tag
actual = actual.replace(/ [{}\|]>/g, '>'); // intra-tag
actual = actual.replace(/[\[\]\^{}\|]/g, ''); // outside tag
return (expected == actual) ? RESULT_SEL : RESULT_DIFF;
}
/**
* Compare the current HTMLtest result to the expectation string(s).
*
* @param actual {String/Boolean} actual value
* @param expected {String/Array} expectation(s)
* @param emitFlags {Object} flags to use for canonicalization
* @return {Integer} one of the RESULT_... return values
* @see variables.js for return values
*/
function compareHTMLToExpectation(actual, expected, emitFlags) {
// Find the most favorable result among the possible expectation strings.
var expectedArr = getExpectationArray(expected);
var count = expectedArr ? expectedArr.length : 0;
var best = RESULT_DIFF;
for (var idx = 0; idx < count && best < RESULT_EQUAL; ++idx) {
var expected = expectedArr[idx];
expected = canonicalizeSpaces(expected);
expected = canonicalizeElementsAndAttributes(expected, emitFlags);
var singleResult = compareHTMLToSingleExpectation(expected, actual);
best = Math.max(best, singleResult);
}
return best;
}
/**
* Compare the current HTMLtest result to expected and acceptable results
*
* @param expected {String/Array} expected result(s)
* @param accepted {String/Array} accepted result(s)
* @param actual {String} actual result
* @param emitFlags {Object} how to canonicalize the HTML strings
* @param result {Object} [out] object recieving the result of the comparison.
*/
function compareHTMLTestResultTo(expected, accepted, actual, emitFlags, result) {
actual = actual.replace(/[\x60\xb4]/g, '');
actual = canonicalizeElementsAndAttributes(actual, emitFlags);
var bestExpected = compareHTMLToExpectation(actual, expected, emitFlags);
if (bestExpected == RESULT_EQUAL) {
// Shortcut - it doesn't get any better
result.valresult = VALRESULT_EQUAL;
result.selresult = SELRESULT_EQUAL;
return;
}
var bestAccepted = compareHTMLToExpectation(actual, accepted, emitFlags);
switch (bestExpected) {
case RESULT_SEL:
switch (bestAccepted) {
case RESULT_EQUAL:
// The HTML was equal to the/an expected HTML result as well
// (just not the selection there), therefore the difference
// between expected and accepted can only lie in the selection.
result.valresult = VALRESULT_EQUAL;
result.selresult = SELRESULT_ACCEPT;
return;
case RESULT_SEL:
case RESULT_DIFF:
// The acceptable expectations did not yield a better result
// -> stay with the original (i.e., comparison to 'expected') result.
result.valresult = VALRESULT_EQUAL;
result.selresult = SELRESULT_DIFF;
return;
}
break;
case RESULT_DIFF:
switch (bestAccepted) {
case RESULT_EQUAL:
result.valresult = VALRESULT_ACCEPT;
result.selresult = SELRESULT_EQUAL;
return;
case RESULT_SEL:
result.valresult = VALRESULT_ACCEPT;
result.selresult = SELRESULT_DIFF;
return;
case RESULT_DIFF:
result.valresult = VALRESULT_DIFF;
result.selresult = SELRESULT_NA;
return;
}
break;
}
throw INTERNAL_ERR + HTML_COMPARISON;
}
/**
* Verify that the canaries are unviolated.
*
* @param container {Object} the test container descriptor as object reference
* @param result {Object} object reference that contains the result data
* @return {Boolean} whether the canaries' HTML is OK (selection flagged, but not fatal)
*/
function verifyCanaries(container, result) {
if (!container.canary) {
return true;
}
var str = canonicalizeElementsAndAttributes(result.bodyInnerHTML, emitFlagsForCanary);
if (str.length < 2 * container.canary.length) {
result.valresult = VALRESULT_CANARY;
result.selresult = SELRESULT_NA;
result.output = result.bodyOuterHTML;
return false;
}
var strBefore = str.substr(0, container.canary.length);
var strAfter = str.substr(str.length - container.canary.length);
// Verify that the canary stretch doesn't contain any selection markers
if (SELECTION_MARKERS.test(strBefore) || SELECTION_MARKERS.test(strAfter)) {
str = str.replace(SELECTION_MARKERS, '');
if (str.length < 2 * container.canary.length) {
result.valresult = VALRESULT_CANARY;
result.selresult = SELRESULT_NA;
result.output = result.bodyOuterHTML;
return false;
}
// Selection escaped contentEditable element, but HTML may still be ok.
result.selresult = SELRESULT_CANARY;
strBefore = str.substr(0, container.canary.length);
strAfter = str.substr(str.length - container.canary.length);
}
if (strBefore !== container.canary || strAfter !== container.canary) {
result.valresult = VALRESULT_CANARY;
result.selresult = SELRESULT_NA;
result.output = result.bodyOuterHTML;
return false;
}
return true;
}
/**
* Compare the current HTMLtest result to the expectation string(s).
* Sets the global result variables.
*
* @param suite {Object} the test suite as object reference
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} the test as object reference
* @param container {Object} the test container description
* @param result {Object} [in/out] the result description, incl. HTML strings
* @see variables.js for result values
*/
function compareHTMLTestResult(suite, group, test, container, result) {
if (!verifyCanaries(container, result)) {
return;
}
var emitFlags = {
emitAttrs: getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES),
emitStyle: getTestParameter(suite, group, test, PARAM_CHECK_STYLE),
emitClass: getTestParameter(suite, group, test, PARAM_CHECK_CLASS),
emitID: getTestParameter(suite, group, test, PARAM_CHECK_ID),
lowercase: true,
canonicalizeUnits: true
};
// 2a.) Compare opening tag -
// decide whether to compare vs. outer or inner HTML based on this.
var openingTagEnd = result.outerHTML.indexOf('>') + 1;
var openingTag = result.outerHTML.substr(0, openingTagEnd);
openingTag = canonicalizeElementsAndAttributes(openingTag, emitFlags);
var tagCmp = compareHTMLToExpectation(openingTag, container.tagOpen, emitFlags);
if (tagCmp == RESULT_EQUAL) {
result.output = result.innerHTML;
compareHTMLTestResultTo(
getTestParameter(suite, group, test, PARAM_EXPECTED),
getTestParameter(suite, group, test, PARAM_ACCEPT),
result.innerHTML,
emitFlags,
result)
} else {
result.output = result.outerHTML;
compareHTMLTestResultTo(
getContainerParameter(suite, group, test, container, PARAM_EXPECTED_OUTER),
getContainerParameter(suite, group, test, container, PARAM_ACCEPT_OUTER),
result.outerHTML,
emitFlags,
result)
}
}
/**
* Insert a selection position indicator.
*
* @param node {DOMNode} the node where to insert the selection indicator
* @param offs {Integer} the offset of the selection indicator
* @param textInd {String} the indicator to use if the node is a text node
* @param elemInd {String} the indicator to use if the node is an element node
*/
function insertSelectionIndicator(node, offs, textInd, elemInd) {
switch (node.nodeType) {
case DOM_NODE_TYPE_TEXT:
// Insert selection marker for text node into text content.
var text = node.data;
node.data = text.substring(0, offs) + textInd + text.substring(offs);
break;
case DOM_NODE_TYPE_ELEMENT:
var child = node.firstChild;
try {
// node has other children: insert marker as comment node
var comment = document.createComment(elemInd);
while (child && offs) {
--offs;
child = child.nextSibling;
}
if (child) {
node.insertBefore(comment, child);
} else {
node.appendChild(comment);
}
} catch (ex) {
// can't append child comment -> insert as special attribute(s)
switch (elemInd) {
case '|':
node.setAttribute(ATTRNAME_SEL_START, '1');
node.setAttribute(ATTRNAME_SEL_END, '1');
break;
case '{':
node.setAttribute(ATTRNAME_SEL_START, '1');
break;
case '}':
node.setAttribute(ATTRNAME_SEL_END, '1');
break;
}
}
break;
}
}
/**
* Adds quotes around all text nodes to show cases with non-normalized
* text nodes. Those are not a bug, but may still be usefil in helping to
* debug erroneous cases.
*
* @param node {DOMNode} root node from which to descend
*/
function encloseTextNodesWithQuotes(node) {
switch (node.nodeType) {
case DOM_NODE_TYPE_ELEMENT:
for (var i = 0; i < node.childNodes.length; ++i) {
encloseTextNodesWithQuotes(node.childNodes[i]);
}
break;
case DOM_NODE_TYPE_TEXT:
node.data = '\x60' + node.data + '\xb4';
break;
}
}
/**
* Retrieve the result of a test run and do some preliminary canonicalization.
*
* @param container {Object} the container where to retrieve the result from as object reference
* @param result {Object} object reference that contains the result data
* @return {String} a preliminarily canonicalized innerHTML with selection markers
*/
function prepareHTMLTestResult(container, result) {
// Start with empty strings in case any of the below throws.
result.innerHTML = '';
result.outerHTML = '';
// 1.) insert selection markers
var selRange = createFromWindow(container.win);
if (selRange) {
// save values, since range object gets auto-modified
var node1 = selRange.getAnchorNode();
var offs1 = selRange.getAnchorOffset();
var node2 = selRange.getFocusNode();
var offs2 = selRange.getFocusOffset();
// add markers
if (node1 && node1 == node2 && offs1 == offs2) {
// collapsed selection
insertSelectionIndicator(node1, offs1, '^', '|');
} else {
// Start point and end point are different
if (node1) {
insertSelectionIndicator(node1, offs1, '[', '{');
}
if (node2) {
if (node1 == node2 && offs1 < offs2) {
// Anchor indicator was inserted under the same node, so we need
// to shift the offset by 1
++offs2;
}
insertSelectionIndicator(node2, offs2, ']', '}');
}
}
}
// 2.) insert markers for text node boundaries;
encloseTextNodesWithQuotes(container.editor);
// 3.) retrieve inner and outer HTML
result.innerHTML = initialCanonicalizationOf(container.editor.innerHTML);
result.bodyInnerHTML = initialCanonicalizationOf(container.body.innerHTML);
if (goog.userAgent.IE) {
result.outerHTML = initialCanonicalizationOf(container.editor.outerHTML);
result.bodyOuterHTML = initialCanonicalizationOf(container.body.outerHTML);
result.outerHTML = result.outerHTML.replace(/^\s+/, '');
result.outerHTML = result.outerHTML.replace(/\s+$/, '');
result.bodyOuterHTML = result.bodyOuterHTML.replace(/^\s+/, '');
result.bodyOuterHTML = result.bodyOuterHTML.replace(/\s+$/, '');
} else {
result.outerHTML = initialCanonicalizationOf(new XMLSerializer().serializeToString(container.editor));
result.bodyOuterHTML = initialCanonicalizationOf(new XMLSerializer().serializeToString(container.body));
}
}
/**
* Compare a text test result to the expectation string(s).
*
* @param suite {Object} the test suite as object reference
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} the test as object reference
* @param actual {String/Boolean} actual value
* @param expected {String/Array} expectation(s)
* @return {Boolean} whether we found a match
*/
function compareTextTestResultWith(suite, group, test, actual, expected) {
var expectedArr = getExpectationArray(expected);
// Find the most favorable result among the possible expectation strings.
var count = expectedArr.length;
// If the value matches the expectation exactly, then we're fine.
for (var idx = 0; idx < count; ++idx) {
if (actual === expectedArr[idx])
return true;
}
// Otherwise see if we should canonicalize specific value types.
//
// We only need to look at font name, color and size units if the originating
// test was both a) queryCommandValue and b) querying a font name/color/size
// specific criterion.
//
// TODO(rolandsteiner): This is ugly! Refactor!
switch (getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
case 'backcolor':
case 'forecolor':
case 'hilitecolor':
for (var idx = 0; idx < count; ++idx) {
if (new Color(actual).compare(new Color(expectedArr[idx])))
return true;
}
return false;
case 'fontname':
for (var idx = 0; idx < count; ++idx) {
if (new FontName(actual).compare(new FontName(expectedArr[idx])))
return true;
}
return false;
case 'fontsize':
for (var idx = 0; idx < count; ++idx) {
if (new FontSize(actual).compare(new FontSize(expectedArr[idx])))
return true;
}
return false;
}
return false;
}
/**
* Compare the passed-in text test result to the expectation string(s).
* Sets the global result variables.
*
* @param suite {Object} the test suite as object reference
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} the test as object reference
* @param actual {String/Boolean} actual value
* @return {Integer} a RESUTLHTML... result value
* @see variables.js for result values
*/
function compareTextTestResult(suite, group, test, result) {
var expected = getTestParameter(suite, group, test, PARAM_EXPECTED);
if (compareTextTestResultWith(suite, group, test, result.output, expected)) {
result.valresult = VALRESULT_EQUAL;
return;
}
var accepted = getTestParameter(suite, group, test, PARAM_ACCEPT);
if (accepted && compareTextTestResultWith(suite, group, test, result.output, accepted)) {
result.valresult = VALRESULT_ACCEPT;
return;
}
result.valresult = VALRESULT_DIFF;
}

View File

@ -0,0 +1,456 @@
/**
* @fileoverview
* Functions used to format the test result output.
*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the 'License')
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @version 0.1
* @author rolandsteiner@google.com
*/
/**
* Writes a fatal error to the output (replaces alert box)
*
* @param text {String} text to output
*/
function writeFatalError(text) {
var errorsStart = document.getElementById('errors');
var divider = document.getElementById('divider');
if (!errorsStart) {
errorsStart = document.createElement('hr');
errorsStart.id = 'errors';
divider.parentNode.insertBefore(errorsStart, divider);
}
var error = document.createElement('div');
error.className = 'fatalerror';
error.innerHTML = 'FATAL ERROR: ' + escapeOutput(text);
errorsStart.parentNode.insertBefore(error, divider);
}
/**
* Generates a unique ID for a given single test out of the suite ID and
* test ID.
*
* @param suiteID {string} ID string of the suite
* @param testID {string} ID string of the individual tests
* @return {string} globally unique ID
*/
function generateOutputID(suiteID, testID) {
return commonIDPrefix + '-' + suiteID + '_' + testID;
}
/**
* Function to highlight the selection markers
*
* @param str {String} a HTML string containing selection markers
* @return {String} the HTML string with highlighting tags around the markers
*/
function highlightSelectionMarkers(str) {
str = str.replace(/\[/g, '<span class="sel">[</span>');
str = str.replace(/\]/g, '<span class="sel">]</span>');
str = str.replace(/\^/g, '<span class="sel">^</span>');
str = str.replace(/{/g, '<span class="sel">{</span>');
str = str.replace(/}/g, '<span class="sel">}</span>');
str = str.replace(/\|/g, '<b class="sel">|</b>');
return str;
}
/**
* Function to highlight the selection markers
*
* @param str {String} a HTML string containing selection markers
* @return {String} the HTML string with highlighting tags around the markers
*/
function highlightSelectionMarkersAndTextNodes(str) {
str = highlightSelectionMarkers(str);
str = str.replace(/\x60/g, '<span class="txt">');
str = str.replace(/\xb4/g, '</span>');
return str;
}
/**
* Function to format output according to type
*
* @param value {String/Boolean} string or value to format
* @return {String} HTML-formatted string
*/
function formatValueOrString(value) {
if (value === undefined)
return '<i>undefined</i>';
if (value === null)
return '<i>null</i>';
switch (typeof value) {
case 'boolean':
return '<i>' + value.toString() + '</i>';
case 'number':
return value.toString();
case 'string':
return "'" + escapeOutput(value) + "'";
default:
return '<i>(' + escapeOutput(value.toString()) + ')</i>';
}
}
/**
* Function to highlight text nodes
*
* @param suite {Object} the suite the test belongs to
* @param group {Object} the group within the suite the test belongs to
* @param test {Object} the test description as object reference
* @param actual {String} a HTML string containing text nodes with markers
* @return {String} string with highlighting tags around the text node parts
*/
function formatActualResult(suite, group, test, actual) {
if (typeof actual != 'string')
return formatValueOrString(actual);
actual = escapeOutput(actual);
// Fade attributes (or just style) if not actually tested for
if (!getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES)) {
actual = actual.replace(/([^ =]+)=\x22([^\x22]*)\x22/g, '<span class="fade">$1="$2"</span>');
} else {
// NOTE: convert 'class="..."' first, before adding other <span class="fade">...</span> !!!
if (!getTestParameter(suite, group, test, PARAM_CHECK_CLASS)) {
actual = actual.replace(/class=\x22([^\x22]*)\x22/g, '<span class="fade">class="$1"</span>');
}
if (!getTestParameter(suite, group, test, PARAM_CHECK_STYLE)) {
actual = actual.replace(/style=\x22([^\x22]*)\x22/g, '<span class="fade">style="$1"</span>');
}
if (!getTestParameter(suite, group, test, PARAM_CHECK_ID)) {
actual = actual.replace(/id=\x22([^\x22]*)\x22/g, '<span class="fade">id="$1"</span>');
} else {
// fade out contenteditable host element's 'editor-<xyz>' ID.
actual = actual.replace(/id=\x22editor-([^\x22]*)\x22/g, '<span class="fade">id="editor-$1"</span>');
}
// grey out 'xmlns'
actual = actual.replace(/xmlns=\x22([^\x22]*)\x22/g, '<span class="fade">xmlns="$1"</span>');
// remove 'onload'
actual = actual.replace(/onload=\x22[^\x22]*\x22 ?/g, '');
}
// Highlight selection markers and text nodes.
actual = highlightSelectionMarkersAndTextNodes(actual);
return actual;
}
/**
* Escape text content for use with .innerHTML.
*
* @param str {String} HTML text to displayed
* @return {String} the escaped HTML
*/
function escapeOutput(str) {
return str ? str.replace(/\</g, '&lt;').replace(/\>/g, '&gt;') : '';
}
/**
* Fills in a single output table cell
*
* @param id {String} ID of the table cell
* @param val {String} inner HTML to set
* @param ttl {String, optional} value of the 'title' attribute
* @param cls {String, optional} class name for the cell
*/
function setTD(id, val, ttl, cls) {
var td = document.getElementById(id);
if (td) {
td.innerHTML = val;
if (ttl) {
td.title = ttl;
}
if (cls) {
td.className = cls;
}
}
}
/**
* Outputs the results of a single test suite
*
* @param suite {Object} test suite as object reference
* @param clsID {String} test class ID ('Proposed', 'RFC', 'Final')
* @param group {Object} the group of tests within the suite the test belongs to
* @param testIdx {Object} the test as object reference
*/
function outputTestResults(suite, clsID, group, test) {
var suiteID = suite.id;
var cls = suite[clsID];
var trID = generateOutputID(suiteID, test.id);
var testResult = results[suiteID][clsID][test.id];
var testValOut = VALOUTPUT[testResult.valresult];
var testSelOut = SELOUTPUT[testResult.selresult];
var suiteChecksSelOnly = !suiteChecksHTMLOrText(suite);
var testUsesHTML = !!getTestParameter(suite, group, test, PARAM_EXECCOMMAND) ||
!!getTestParameter(suite, group, test, PARAM_FUNCTION);
// Set background color for test ID
var td = document.getElementById(trID + IDOUT_TESTID);
if (td) {
td.className = (suiteChecksSelOnly && testResult.selresult != SELRESULT_NA) ? testSelOut.css : testValOut.css;
}
// Fill in "Command" and "Value" cells
var cmd;
var cmdOutput = '&nbsp;';
var valOutput = '&nbsp;';
if (cmd = getTestParameter(suite, group, test, PARAM_EXECCOMMAND)) {
cmdOutput = escapeOutput(cmd);
var val = getTestParameter(suite, group, test, PARAM_VALUE);
if (val !== undefined) {
valOutput = formatValueOrString(val);
}
} else if (cmd = getTestParameter(suite, group, test, PARAM_FUNCTION)) {
cmdOutput = '<i>' + escapeOutput(cmd) + '</i>';
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSUPPORTED)) {
cmdOutput = '<i>queryCommandSupported</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDENABLED)) {
cmdOutput = '<i>queryCommandEnabled</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDINDETERM)) {
cmdOutput = '<i>queryCommandIndeterm</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSTATE)) {
cmdOutput = '<i>queryCommandState</i>';
valOutput = escapeOutput(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
cmdOutput = '<i>queryCommandValue</i>';
valOutput = escapeOutput(cmd);
} else {
cmdOutput = '<i>(none)</i>';
}
setTD(trID + IDOUT_COMMAND, cmdOutput);
setTD(trID + IDOUT_VALUE, valOutput);
// Fill in "Attribute checked?" and "Style checked?" cells
if (testUsesHTML) {
var checkAttrs = getTestParameter(suite, group, test, PARAM_CHECK_ATTRIBUTES);
var checkStyle = getTestParameter(suite, group, test, PARAM_CHECK_STYLE);
setTD(trID + IDOUT_CHECKATTRS,
checkAttrs ? OUTSTR_YES : OUTSTR_NO,
checkAttrs ? 'attributes must match' : 'attributes are ignored');
if (checkAttrs && checkStyle) {
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_YES, 'style attribute contents must match');
} else if (checkAttrs) {
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NO, 'style attribute contents is ignored');
} else {
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NO, 'all attributes (incl. style) are ignored');
}
} else {
setTD(trID + IDOUT_CHECKATTRS, OUTSTR_NA, 'attributes not applicable');
setTD(trID + IDOUT_CHECKSTYLE, OUTSTR_NA, 'style not applicable');
}
// Fill in test pad specification cell (initial HTML + selection markers)
setTD(trID + IDOUT_PAD, highlightSelectionMarkers(escapeOutput(getTestParameter(suite, group, test, PARAM_PAD))));
// Fill in expected result(s) cell
var expectedOutput = '';
var expectedArr = getExpectationArray(getTestParameter(suite, group, test, PARAM_EXPECTED));
for (var idx = 0; idx < expectedArr.length; ++idx) {
if (expectedOutput) {
expectedOutput += '\xA0\xA0\xA0<i>or</i><br>';
}
expectedOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(expectedArr[idx]))
: formatValueOrString(expectedArr[idx]);
}
var acceptedArr = getExpectationArray(getTestParameter(suite, group, test, PARAM_ACCEPT));
for (var idx = 0; idx < acceptedArr.length; ++idx) {
expectedOutput += '<span class="accexp">\xA0\xA0\xA0<i>or</i></span><br><span class="accexp">';
expectedOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(acceptedArr[idx]))
: formatValueOrString(acceptedArr[idx]);
expectedOutput += '</span>';
}
// TODO(rolandsteiner): THIS IS UGLY, relying on 'div' container being index 2,
// AND not allowing other containers to have 'outer' results - change!!!
var outerOutput = '';
expectedArr = getExpectationArray(getContainerParameter(suite, group, test, containers[2], PARAM_EXPECTED_OUTER));
for (var idx = 0; idx < expectedArr.length; ++idx) {
if (outerOutput) {
outerOutput += '\xA0\xA0\xA0<i>or</i><br>';
}
outerOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(expectedArr[idx]))
: formatValueOrString(expectedArr[idx]);
}
acceptedArr = getExpectationArray(getContainerParameter(suite, group, test, containers[2], PARAM_ACCEPT_OUTER));
for (var idx = 0; idx < acceptedArr.length; ++idx) {
if (outerOutput) {
outerOutput += '<span class="accexp">\xA0\xA0\xA0<i>or</i></span><br>';
}
outerOutput += '<span class="accexp">';
outerOutput += testUsesHTML ? highlightSelectionMarkers(escapeOutput(acceptedArr[idx]))
: formatValueOrString(acceptedArr[idx]);
outerOutput += '</span>';
}
if (outerOutput) {
expectedOutput += '<hr>' + outerOutput;
}
setTD(trID + IDOUT_EXPECTED, expectedOutput);
// Iterate over the individual container results
for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
var cntID = containers[cntIdx].id;
var cntTD = document.getElementById(trID + IDOUT_CONTAINER + cntID);
var cntResult = testResult[cntID];
var cntValOut = VALOUTPUT[cntResult.valresult];
var cntSelOut = SELOUTPUT[cntResult.selresult];
var cssVal = cntValOut.css;
var cssSel = (!suiteChecksSelOnly || cntResult.selresult != SELRESULT_NA) ? cntSelOut.css : cssVal;
var cssCnt = cssVal;
// Fill in result status cell ("PASS", "ACC.", "FAIL", "EXC.", etc.)
setTD(trID + IDOUT_STATUSVAL + cntID, cntValOut.output, cntValOut.title, cssVal);
// Fill in selection status cell ("PASS", "ACC.", "FAIL", "N/A")
setTD(trID + IDOUT_STATUSSEL + cntID, cntSelOut.output, cntSelOut.title, cssSel);
// Fill in actual result
switch (cntResult.valresult) {
case VALRESULT_SETUP_EXCEPTION:
setTD(trID + IDOUT_ACTUAL + cntID,
SETUP_EXCEPTION + '(mouseover)',
escapeOutput(cntResult.output),
cssVal);
break;
case VALRESULT_EXECUTION_EXCEPTION:
setTD(trID + IDOUT_ACTUAL + cntID,
EXECUTION_EXCEPTION + '(mouseover)',
escapeOutput(cntResult.output.toString()),
cssVal);
break;
case VALRESULT_VERIFICATION_EXCEPTION:
setTD(trID + IDOUT_ACTUAL + cntID,
VERIFICATION_EXCEPTION + '(mouseover)',
escapeOutput(cntResult.output.toString()),
cssVal);
break;
case VALRESULT_UNSUPPORTED:
setTD(trID + IDOUT_ACTUAL + cntID,
escapeOutput(cntResult.output),
'',
cssVal);
break;
case VALRESULT_CANARY:
setTD(trID + IDOUT_ACTUAL + cntID,
highlightSelectionMarkersAndTextNodes(escapeOutput(cntResult.output)),
'',
cssVal);
break;
case VALRESULT_DIFF:
case VALRESULT_ACCEPT:
case VALRESULT_EQUAL:
if (!testUsesHTML) {
setTD(trID + IDOUT_ACTUAL + cntID,
formatValueOrString(cntResult.output),
'',
cssVal);
} else if (cntResult.selresult == SELRESULT_CANARY) {
cssCnt = suiteChecksSelOnly ? cssSel : cssVal;
setTD(trID + IDOUT_ACTUAL + cntID,
highlightSelectionMarkersAndTextNodes(escapeOutput(cntResult.output)),
'',
cssCnt);
} else {
cssCnt = suiteChecksSelOnly ? cssSel : cssVal;
setTD(trID + IDOUT_ACTUAL + cntID,
formatActualResult(suite, group, test, cntResult.output),
'',
cssCnt);
}
break;
default:
cssCnt = 'exception';
setTD(trID + IDOUT_ACTUAL + cntID,
INTERNAL_ERR + 'UNKNOWN RESULT VALUE',
'',
cssCnt);
}
if (cntTD) {
cntTD.className = cssCnt;
}
}
}
/**
* Outputs the results of a single test suite
*
* @param {Object} suite as object reference
*/
function outputTestSuiteResults(suite) {
var suiteID = suite.id;
var span;
span = document.getElementById(suiteID + '-score');
if (span) {
span.innerHTML = results[suiteID].valscore + '/' + results[suiteID].count;
}
span = document.getElementById(suiteID + '-selscore');
if (span) {
span.innerHTML = results[suiteID].selscore + '/' + results[suiteID].count;
}
span = document.getElementById(suiteID + '-time');
if (span) {
span.innerHTML = results[suiteID].time;
}
span = document.getElementById(suiteID + '-progress');
if (span) {
span.style.color = 'green';
}
for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
var clsID = testClassIDs[clsIdx];
var cls = suite[clsID];
if (!cls)
continue;
span = document.getElementById(suiteID + '-' + clsID + '-score');
if (span) {
span.innerHTML = results[suiteID][clsID].valscore + '/' + results[suiteID][clsID].count;
}
span = document.getElementById(suiteID + '-' + clsID + '-selscore');
if (span) {
span.innerHTML = results[suiteID][clsID].selscore + '/' + results[suiteID][clsID].count;
}
var groupCount = cls.length;
for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
var group = cls[groupIdx];
var testCount = group.tests.length;
for (var testIdx = 0; testIdx < testCount; ++testIdx) {
var test = group.tests[testIdx];
outputTestResults(suite, clsID, group, test);
}
}
}
}

View File

@ -0,0 +1,269 @@
/**
* @fileoverview
* Functions used to handle test and expectation strings.
*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the 'License')
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @version 0.1
* @author rolandsteiner@google.com
*/
/**
* Normalize text selection indicators and convert inter-element selection
* indicators to comments.
*
* Note that this function relies on the spaces of the input string already
* having been normalized by canonicalizeSpaces!
*
* @param pad {String} HTML string that includes selection marker characters
* @return {String} the HTML string with the selection markers converted
*/
function convertSelectionIndicators(pad) {
// Sanity check: Markers { } | only directly before or after an element,
// or just before a closing > (i.e., not within a text node).
// Note that intra-tag selection markers have already been converted to the
// special selection attribute(s) above.
if (/[^>][{}\|][^<>]/.test(pad) ||
/^[{}\|][^<]/.test(pad) ||
/[^>][{}\|]$/.test(pad) ||
/^[{}\|]*$/.test(pad)) {
throw SETUP_BAD_SELECTION_SPEC;
}
// Convert intra-tag selection markers to special attributes.
pad = pad.replace(/\{\>/g, ATTRNAME_SEL_START + '="1">');
pad = pad.replace(/\}\>/g, ATTRNAME_SEL_END + '="1">');
pad = pad.replace(/\|\>/g, ATTRNAME_SEL_START + '="1" ' +
ATTRNAME_SEL_END + '="1">');
// Convert remaining {, }, | to comments with '[' and ']' data.
pad = pad.replace('{', '<!--[-->');
pad = pad.replace('}', '<!--]-->');
pad = pad.replace('|', '<!--[--><!--]-->');
// Convert caret indicator ^ to empty selection indicator []
// (this simplifies further processing).
pad = pad.replace(/\^/, '[]');
return pad;
}
/**
* Derives one point of the selection from the indicators with the HTML tree:
* '[' or ']' within a text or comment node, or the special selection
* attributes within an element node.
*
* @param root {DOMNode} root node of the recursive search
* @param marker {String} which marker to look for: '[' or ']'
* @return {Object} a pair object: {node: {DOMNode}/null, offset: {Integer}}
*/
function deriveSelectionPoint(root, marker) {
switch (root.nodeType) {
case DOM_NODE_TYPE_ELEMENT:
if (root.attributes) {
// Note: getAttribute() is necessary for this to work on all browsers!
if (marker == '[' && root.getAttribute(ATTRNAME_SEL_START)) {
root.removeAttribute(ATTRNAME_SEL_START);
return {node: root, offs: 0};
}
if (marker == ']' && root.getAttribute(ATTRNAME_SEL_END)) {
root.removeAttribute(ATTRNAME_SEL_END);
return {node: root, offs: 0};
}
}
for (var i = 0; i < root.childNodes.length; ++i) {
var pair = deriveSelectionPoint(root.childNodes[i], marker);
if (pair.node) {
return pair;
}
}
break;
case DOM_NODE_TYPE_TEXT:
var pos = root.data.indexOf(marker);
if (pos != -1) {
// Remove selection marker from text.
var nodeText = root.data;
root.data = nodeText.substr(0, pos) + nodeText.substr(pos + 1);
return {node: root, offs: pos };
}
break;
case DOM_NODE_TYPE_COMMENT:
var pos = root.data.indexOf(marker);
if (pos != -1) {
// Remove comment node from parent.
var helper = root.previousSibling;
for (pos = 0; helper; ++pos ) {
helper = helper.previousSibling;
}
helper = root;
root = root.parentNode;
root.removeChild(helper);
return {node: root, offs: pos };
}
break;
}
return {node: null, offs: 0 };
}
/**
* Initialize the test HTML with the starting state specified in the test.
*
* The selection is specified "inline", using special characters:
* ^ a collapsed text caret selection (same as [])
* [ the selection start within a text node
* ] the selection end within a text node
* | collapsed selection between elements (same as {})
* { selection starting with the following element
* } selection ending with the preceding element
* {, } and | can also be used within an element tag, just before the closing
* angle bracket > to specify a selection [element, 0] where the element
* doesn't otherwise have any children. Ex.: <hr {>foobarbaz<hr }>
*
* Explicit and implicit specification can also be mixed between the 2 points.
*
* A pad string must only contain at most ONE of the above that is suitable for
* that start or end point, respectively, and must contain either both or none.
*
* @param suite {Object} suite that test originates in as object reference
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} test to be run as object reference
* @param container {Object} container descriptor as object reference
*/
function initContainer(suite, group, test, container) {
var pad = getTestParameter(suite, group, test, PARAM_PAD);
pad = canonicalizeSpaces(pad);
pad = convertSelectionIndicators(pad);
if (container.editorID) {
container.body.innerHTML = container.canary + container.tagOpen + pad + container.tagClose + container.canary;
container.editor = container.doc.getElementById(container.editorID);
} else {
container.body.innerHTML = pad;
container.editor = container.body;
}
win = container.win;
doc = container.doc;
body = container.body;
editor = container.editor;
sel = null;
if (!editor) {
throw SETUP_CONTAINER;
}
if (getTestParameter(suite, group, test, PARAM_STYLE_WITH_CSS)) {
try {
container.doc.execCommand('styleWithCSS', false, true);
} catch (ex) {
// ignore exception if unsupported
}
}
var selAnchor = deriveSelectionPoint(editor, '[');
var selFocus = deriveSelectionPoint(editor, ']');
// sanity check
if (!selAnchor || !selFocus) {
throw SETUP_SELECTION;
}
if (!selAnchor.node || !selFocus.node) {
if (selAnchor.node || selFocus.node) {
// Broken test: only one selection point was specified
throw SETUP_BAD_SELECTION_SPEC;
}
sel = null;
return;
}
if (selAnchor.node === selFocus.node) {
if (selAnchor.offs > selFocus.offs) {
// Both selection points are within the same node, the selection was
// specified inline (thus the end indicator element or character was
// removed), and the end point is before the start (reversed selection).
// Start offset that was derived is now off by 1 and needs adjustment.
--selAnchor.offs;
}
if (selAnchor.offs === selFocus.offs) {
createCaret(selAnchor.node, selAnchor.offs).select();
try {
sel = win.getSelection();
} catch (ex) {
sel = undefined;
}
return;
}
}
createFromNodes(selAnchor.node, selAnchor.offs, selFocus.node, selFocus.offs).select();
try {
sel = win.getSelection();
} catch (ex) {
sel = undefined;
}
}
/**
* Reset the editor element after a test is run.
*
* @param container {Object} container descriptor as object reference
*/
function resetContainer(container) {
// Remove errant styles and attributes that may have been set on the <body>.
container.body.removeAttribute('style');
container.body.removeAttribute('color');
container.body.removeAttribute('bgcolor');
try {
container.doc.execCommand('styleWithCSS', false, false);
} catch (ex) {
// Ignore exception if unsupported.
}
}
/**
* Initialize the editor document.
*/
function initEditorDocs() {
for (var c = 0; c < containers.length; ++c) {
var container = containers[c];
container.iframe = document.getElementById('iframe-' + container.id);
container.win = container.iframe.contentWindow;
container.doc = container.win.document;
container.body = container.doc.body;
// container.editor is set per test (changes on embedded editor elements).
// Some browsers require a selection to go with their 'styleWithCSS'.
try {
container.win.getSelection().selectAllChildren(editor);
} catch (ex) {
// ignore exception if unsupported
}
// Default styleWithCSS to false.
try {
container.doc.execCommand('styleWithCSS', false, false);
} catch (ex) {
// ignore exception if unsupported
}
}
}

View File

@ -0,0 +1,5 @@
goog.require('goog.dom.Range');
window.createFromWindow = goog.dom.Range.createFromWindow;
window.createFromNodes = goog.dom.Range.createFromNodes;
window.createCaret = goog.dom.Range.createCaret;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,383 @@
/**
* @fileoverview
* Main functions used in running the RTE test suite.
*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the 'License')
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @version 0.1
* @author rolandsteiner@google.com
*/
/**
* Info function: returns true if the suite (mainly) tests the result HTML/Text.
*
* @param suite {String} the test suite
* @return {boolean} Whether the suite main focus is the output HTML/Text
*/
function suiteChecksHTMLOrText(suite) {
return suite.id[0] != 'S';
}
/**
* Info function: returns true if the suite checks the result selection.
*
* @param suite {String} the test suite
* @return {boolean} Whether the suite checks the selection
*/
function suiteChecksSelection(suite) {
return suite.id[0] != 'Q';
}
/**
* Helper function returning the effective value of a test parameter.
*
* @param suite {Object} the test suite
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} the test
* @param param {String} the test parameter to be checked
* @return {Any} the effective value of the parameter (can be undefined)
*/
function getTestParameter(suite, group, test, param) {
var val = test[param];
if (val === undefined) {
val = group[param];
}
if (val === undefined) {
val = suite[param];
}
return val;
}
/**
* Helper function returning the effective value of a container/test parameter.
*
* @param suite {Object} the test suite
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} the test
* @param container {Object} the container descriptor object
* @param param {String} the test parameter to be checked
* @return {Any} the effective value of the parameter (can be undefined)
*/
function getContainerParameter(suite, group, test, container, param) {
var val = undefined;
if (test[container.id]) {
val = test[container.id][param];
}
if (val === undefined) {
val = test[param];
}
if (val === undefined) {
val = group[param];
}
if (val === undefined) {
val = suite[param];
}
return val;
}
/**
* Initializes the global variables before any tests are run.
*/
function initVariables() {
results = {
count: 0,
valscore: 0,
selscore: 0
};
}
/**
* Runs a single test - outputs and sets the result variables.
*
* @param suite {Object} suite that test originates in as object reference
* @param group {Object} group of tests within the suite the test belongs to
* @param test {Object} test to be run as object reference
* @param container {Object} container descriptor as object reference
* @see variables.js for RESULT... values
*/
function runSingleTest(suite, group, test, container) {
var result = {
valscore: 0,
selscore: 0,
valresult: VALRESULT_NOT_RUN,
selresult: SELRESULT_NOT_RUN,
output: ''
};
// 1.) Populate the editor element with the initial test setup HTML.
try {
initContainer(suite, group, test, container);
} catch(ex) {
result.valresult = VALRESULT_SETUP_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = SETUP_EXCEPTION + ex.toString();
return result;
}
// 2.) Run the test command, general function or query function.
var isHTMLTest = false;
try {
var cmd = undefined;
if (cmd = getTestParameter(suite, group, test, PARAM_EXECCOMMAND)) {
isHTMLTest = true;
// Note: "getTestParameter(suite, group, test, PARAM_VALUE) || null"
// doesn't work, since value might be the empty string, e.g., for 'insertText'!
var value = getTestParameter(suite, group, test, PARAM_VALUE);
if (value === undefined) {
value = null;
}
container.doc.execCommand(cmd, false, value);
} else if (cmd = getTestParameter(suite, group, test, PARAM_FUNCTION)) {
isHTMLTest = true;
eval(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSUPPORTED)) {
result.output = container.doc.queryCommandSupported(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDENABLED)) {
result.output = container.doc.queryCommandEnabled(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDINDETERM)) {
result.output = container.doc.queryCommandIndeterm(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDSTATE)) {
result.output = container.doc.queryCommandState(cmd);
} else if (cmd = getTestParameter(suite, group, test, PARAM_QUERYCOMMANDVALUE)) {
result.output = container.doc.queryCommandValue(cmd);
if (result.output === false) {
// A return value of boolean 'false' for queryCommandValue means 'not supported'.
result.valresult = VALRESULT_UNSUPPORTED;
result.selresult = SELRESULT_NA;
result.output = UNSUPPORTED;
return result;
}
} else {
result.valresult = VALRESULT_SETUP_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = SETUP_EXCEPTION + SETUP_NOCOMMAND;
return result;
}
} catch (ex) {
result.valresult = VALRESULT_EXECUTION_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = EXECUTION_EXCEPTION + ex.toString();
return result;
}
// 4.) Verify test result
try {
if (isHTMLTest) {
// First, retrieve HTML from container
prepareHTMLTestResult(container, result);
// Compare result to expectations
compareHTMLTestResult(suite, group, test, container, result);
result.valscore = (result.valresult === VALRESULT_EQUAL) ? 1 : 0;
result.selscore = (result.selresult === SELRESULT_EQUAL) ? 1 : 0;
} else {
compareTextTestResult(suite, group, test, result);
result.selresult = SELRESULT_NA;
result.valscore = (result.valresult === VALRESULT_EQUAL) ? 1 : 0;
}
} catch (ex) {
result.valresult = VALRESULT_VERIFICATION_EXCEPTION;
result.selresult = SELRESULT_NA;
result.output = VERIFICATION_EXCEPTION + ex.toString();
return result;
}
return result;
}
/**
* Initializes the results dictionary for a given test suite.
* (for all classes -> tests -> containers)
*
* @param {Object} suite as object reference
*/
function initTestSuiteResults(suite) {
var suiteID = suite.id;
// Initialize results entries for this suite
results[suiteID] = {
count: 0,
valscore: 0,
selscore: 0,
time: 0
};
var totalTestCount = 0;
for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
var clsID = testClassIDs[clsIdx];
var cls = suite[clsID];
if (!cls)
continue;
results[suiteID][clsID] = {
count: 0,
valscore: 0,
selscore: 0
};
var clsTestCount = 0;
var groupCount = cls.length;
for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
var group = cls[groupIdx];
var testCount = group.tests.length;
clsTestCount += testCount;
totalTestCount += testCount;
for (var testIdx = 0; testIdx < testCount; ++testIdx) {
var test = group.tests[testIdx];
results[suiteID][clsID ][test.id] = {
valscore: 0,
selscore: 0,
valresult: VALRESULT_NOT_RUN,
selresult: SELRESULT_NOT_RUN
};
for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
var cntID = containers[cntIdx].id;
results[suiteID][clsID][test.id][cntID] = {
valscore: 0,
selscore: 0,
valresult: VALRESULT_NOT_RUN,
selresult: SELRESULT_NOT_RUN,
output: ''
}
}
}
}
results[suiteID][clsID].count = clsTestCount;
}
results[suiteID].count = totalTestCount;
}
/**
* Runs a single test suite (such as DELETE tests or INSERT tests).
*
* @param suite {Object} suite as object reference
*/
function runTestSuite(suite) {
var suiteID = suite.id;
var suiteStartTime = new Date().getTime();
initTestSuiteResults(suite);
for (var clsIdx = 0; clsIdx < testClassCount; ++clsIdx) {
var clsID = testClassIDs[clsIdx];
var cls = suite[clsID];
if (!cls)
continue;
var groupCount = cls.length;
for (var groupIdx = 0; groupIdx < groupCount; ++groupIdx) {
var group = cls[groupIdx];
var testCount = group.tests.length;
for (var testIdx = 0; testIdx < testCount; ++testIdx) {
var test = group.tests[testIdx];
var valscore = 1;
var selscore = 1;
var valresult = VALRESULT_EQUAL;
var selresult = SELRESULT_EQUAL;
for (var cntIdx = 0; cntIdx < containers.length; ++cntIdx) {
var container = containers[cntIdx];
var cntID = container.id;
var result = runSingleTest(suite, group, test, container);
results[suiteID][clsID][test.id][cntID] = result;
valscore = Math.min(valscore, result.valscore);
selscore = Math.min(selscore, result.selscore);
valresult = Math.min(valresult, result.valresult);
selresult = Math.min(selresult, result.selresult);
resetContainer(container);
}
results[suiteID][clsID][test.id].valscore = valscore;
results[suiteID][clsID][test.id].selscore = selscore;
results[suiteID][clsID][test.id].valresult = valresult;
results[suiteID][clsID][test.id].selresult = selresult;
results[suiteID][clsID].valscore += valscore;
results[suiteID][clsID].selscore += selscore;
results[suiteID].valscore += valscore;
results[suiteID].selscore += selscore;
results.valscore += valscore;
results.selscore += selscore;
}
}
}
results[suiteID].time = new Date().getTime() - suiteStartTime;
}
/**
* Runs a single test suite (such as DELETE tests or INSERT tests)
* and updates the output HTML.
*
* @param {Object} suite as object reference
*/
function runAndOutputTestSuite(suite) {
runTestSuite(suite);
outputTestSuiteResults(suite);
}
/**
* Fills the beacon with the test results.
*/
function fillResults() {
// Result totals of the individual categories
categoryTotals = [
'selection=' + results['S'].selscore,
'apply=' + results['A'].valscore,
'applyCSS=' + results['AC'].valscore,
'change=' + results['C'].valscore,
'changeCSS=' + results['CC'].valscore,
'unapply=' + results['U'].valscore,
'unapplyCSS=' + results['UC'].valscore,
'delete=' + results['D'].valscore,
'forwarddelete=' + results['FD'].valscore,
'insert=' + results['I'].valscore,
'selectionResult=' + (results['A'].selscore +
results['AC'].selscore +
results['C'].selscore +
results['CC'].selscore +
results['U'].selscore +
results['UC'].selscore +
results['D'].selscore +
results['FD'].selscore +
results['I'].selscore),
'querySupported=' + results['Q'].valscore,
'queryEnabled=' + results['QE'].valscore,
'queryIndeterm=' + results['QI'].valscore,
'queryState=' + results['QS'].valscore,
'queryStateCSS=' + results['QSC'].valscore,
'queryValue=' + results['QV'].valscore,
'queryValueCSS=' + results['QVC'].valscore
];
// Beacon copies category results
beacon = categoryTotals.slice(0);
}

View File

@ -0,0 +1,416 @@
/**
* @fileoverview
* Common constants and variables used in the RTE test suite.
*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the 'License')
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @version 0.1
* @author rolandsteiner@google.com
*/
// All colors defined in CSS3.
var colorChart = {
'aliceblue': {red: 0xF0, green: 0xF8, blue: 0xFF},
'antiquewhite': {red: 0xFA, green: 0xEB, blue: 0xD7},
'aqua': {red: 0x00, green: 0xFF, blue: 0xFF},
'aquamarine': {red: 0x7F, green: 0xFF, blue: 0xD4},
'azure': {red: 0xF0, green: 0xFF, blue: 0xFF},
'beige': {red: 0xF5, green: 0xF5, blue: 0xDC},
'bisque': {red: 0xFF, green: 0xE4, blue: 0xC4},
'black': {red: 0x00, green: 0x00, blue: 0x00},
'blanchedalmond': {red: 0xFF, green: 0xEB, blue: 0xCD},
'blue': {red: 0x00, green: 0x00, blue: 0xFF},
'blueviolet': {red: 0x8A, green: 0x2B, blue: 0xE2},
'brown': {red: 0xA5, green: 0x2A, blue: 0x2A},
'burlywood': {red: 0xDE, green: 0xB8, blue: 0x87},
'cadetblue': {red: 0x5F, green: 0x9E, blue: 0xA0},
'chartreuse': {red: 0x7F, green: 0xFF, blue: 0x00},
'chocolate': {red: 0xD2, green: 0x69, blue: 0x1E},
'coral': {red: 0xFF, green: 0x7F, blue: 0x50},
'cornflowerblue': {red: 0x64, green: 0x95, blue: 0xED},
'cornsilk': {red: 0xFF, green: 0xF8, blue: 0xDC},
'crimson': {red: 0xDC, green: 0x14, blue: 0x3C},
'cyan': {red: 0x00, green: 0xFF, blue: 0xFF},
'darkblue': {red: 0x00, green: 0x00, blue: 0x8B},
'darkcyan': {red: 0x00, green: 0x8B, blue: 0x8B},
'darkgoldenrod': {red: 0xB8, green: 0x86, blue: 0x0B},
'darkgray': {red: 0xA9, green: 0xA9, blue: 0xA9},
'darkgreen': {red: 0x00, green: 0x64, blue: 0x00},
'darkgrey': {red: 0xA9, green: 0xA9, blue: 0xA9},
'darkkhaki': {red: 0xBD, green: 0xB7, blue: 0x6B},
'darkmagenta': {red: 0x8B, green: 0x00, blue: 0x8B},
'darkolivegreen': {red: 0x55, green: 0x6B, blue: 0x2F},
'darkorange': {red: 0xFF, green: 0x8C, blue: 0x00},
'darkorchid': {red: 0x99, green: 0x32, blue: 0xCC},
'darkred': {red: 0x8B, green: 0x00, blue: 0x00},
'darksalmon': {red: 0xE9, green: 0x96, blue: 0x7A},
'darkseagreen': {red: 0x8F, green: 0xBC, blue: 0x8F},
'darkslateblue': {red: 0x48, green: 0x3D, blue: 0x8B},
'darkslategray': {red: 0x2F, green: 0x4F, blue: 0x4F},
'darkslategrey': {red: 0x2F, green: 0x4F, blue: 0x4F},
'darkturquoise': {red: 0x00, green: 0xCE, blue: 0xD1},
'darkviolet': {red: 0x94, green: 0x00, blue: 0xD3},
'deeppink': {red: 0xFF, green: 0x14, blue: 0x93},
'deepskyblue': {red: 0x00, green: 0xBF, blue: 0xFF},
'dimgray': {red: 0x69, green: 0x69, blue: 0x69},
'dimgrey': {red: 0x69, green: 0x69, blue: 0x69},
'dodgerblue': {red: 0x1E, green: 0x90, blue: 0xFF},
'firebrick': {red: 0xB2, green: 0x22, blue: 0x22},
'floralwhite': {red: 0xFF, green: 0xFA, blue: 0xF0},
'forestgreen': {red: 0x22, green: 0x8B, blue: 0x22},
'fuchsia': {red: 0xFF, green: 0x00, blue: 0xFF},
'gainsboro': {red: 0xDC, green: 0xDC, blue: 0xDC},
'ghostwhite': {red: 0xF8, green: 0xF8, blue: 0xFF},
'gold': {red: 0xFF, green: 0xD7, blue: 0x00},
'goldenrod': {red: 0xDA, green: 0xA5, blue: 0x20},
'gray': {red: 0x80, green: 0x80, blue: 0x80},
'green': {red: 0x00, green: 0x80, blue: 0x00},
'greenyellow': {red: 0xAD, green: 0xFF, blue: 0x2F},
'grey': {red: 0x80, green: 0x80, blue: 0x80},
'honeydew': {red: 0xF0, green: 0xFF, blue: 0xF0},
'hotpink': {red: 0xFF, green: 0x69, blue: 0xB4},
'indianred': {red: 0xCD, green: 0x5C, blue: 0x5C},
'indigo': {red: 0x4B, green: 0x00, blue: 0x82},
'ivory': {red: 0xFF, green: 0xFF, blue: 0xF0},
'khaki': {red: 0xF0, green: 0xE6, blue: 0x8C},
'lavender': {red: 0xE6, green: 0xE6, blue: 0xFA},
'lavenderblush': {red: 0xFF, green: 0xF0, blue: 0xF5},
'lawngreen': {red: 0x7C, green: 0xFC, blue: 0x00},
'lemonchiffon': {red: 0xFF, green: 0xFA, blue: 0xCD},
'lightblue': {red: 0xAD, green: 0xD8, blue: 0xE6},
'lightcoral': {red: 0xF0, green: 0x80, blue: 0x80},
'lightcyan': {red: 0xE0, green: 0xFF, blue: 0xFF},
'lightgoldenrodyellow': {red: 0xFA, green: 0xFA, blue: 0xD2},
'lightgray': {red: 0xD3, green: 0xD3, blue: 0xD3},
'lightgreen': {red: 0x90, green: 0xEE, blue: 0x90},
'lightgrey': {red: 0xD3, green: 0xD3, blue: 0xD3},
'lightpink': {red: 0xFF, green: 0xB6, blue: 0xC1},
'lightsalmon': {red: 0xFF, green: 0xA0, blue: 0x7A},
'lightseagreen': {red: 0x20, green: 0xB2, blue: 0xAA},
'lightskyblue': {red: 0x87, green: 0xCE, blue: 0xFA},
'lightslategray': {red: 0x77, green: 0x88, blue: 0x99},
'lightslategrey': {red: 0x77, green: 0x88, blue: 0x99},
'lightsteelblue': {red: 0xB0, green: 0xC4, blue: 0xDE},
'lightyellow': {red: 0xFF, green: 0xFF, blue: 0xE0},
'lime': {red: 0x00, green: 0xFF, blue: 0x00},
'limegreen': {red: 0x32, green: 0xCD, blue: 0x32},
'linen': {red: 0xFA, green: 0xF0, blue: 0xE6},
'magenta': {red: 0xFF, green: 0x00, blue: 0xFF},
'maroon': {red: 0x80, green: 0x00, blue: 0x00},
'mediumaquamarine': {red: 0x66, green: 0xCD, blue: 0xAA},
'mediumblue': {red: 0x00, green: 0x00, blue: 0xCD},
'mediumorchid': {red: 0xBA, green: 0x55, blue: 0xD3},
'mediumpurple': {red: 0x93, green: 0x70, blue: 0xDB},
'mediumseagreen': {red: 0x3C, green: 0xB3, blue: 0x71},
'mediumslateblue': {red: 0x7B, green: 0x68, blue: 0xEE},
'mediumspringgreen': {red: 0x00, green: 0xFA, blue: 0x9A},
'mediumturquoise': {red: 0x48, green: 0xD1, blue: 0xCC},
'mediumvioletred': {red: 0xC7, green: 0x15, blue: 0x85},
'midnightblue': {red: 0x19, green: 0x19, blue: 0x70},
'mintcream': {red: 0xF5, green: 0xFF, blue: 0xFA},
'mistyrose': {red: 0xFF, green: 0xE4, blue: 0xE1},
'moccasin': {red: 0xFF, green: 0xE4, blue: 0xB5},
'navajowhite': {red: 0xFF, green: 0xDE, blue: 0xAD},
'navy': {red: 0x00, green: 0x00, blue: 0x80},
'oldlace': {red: 0xFD, green: 0xF5, blue: 0xE6},
'olive': {red: 0x80, green: 0x80, blue: 0x00},
'olivedrab': {red: 0x6B, green: 0x8E, blue: 0x23},
'orange': {red: 0xFF, green: 0xA5, blue: 0x00},
'orangered': {red: 0xFF, green: 0x45, blue: 0x00},
'orchid': {red: 0xDA, green: 0x70, blue: 0xD6},
'palegoldenrod': {red: 0xEE, green: 0xE8, blue: 0xAA},
'palegreen': {red: 0x98, green: 0xFB, blue: 0x98},
'paleturquoise': {red: 0xAF, green: 0xEE, blue: 0xEE},
'palevioletred': {red: 0xDB, green: 0x70, blue: 0x93},
'papayawhip': {red: 0xFF, green: 0xEF, blue: 0xD5},
'peachpuff': {red: 0xFF, green: 0xDA, blue: 0xB9},
'peru': {red: 0xCD, green: 0x85, blue: 0x3F},
'pink': {red: 0xFF, green: 0xC0, blue: 0xCB},
'plum': {red: 0xDD, green: 0xA0, blue: 0xDD},
'powderblue': {red: 0xB0, green: 0xE0, blue: 0xE6},
'purple': {red: 0x80, green: 0x00, blue: 0x80},
'red': {red: 0xFF, green: 0x00, blue: 0x00},
'rosybrown': {red: 0xBC, green: 0x8F, blue: 0x8F},
'royalblue': {red: 0x41, green: 0x69, blue: 0xE1},
'saddlebrown': {red: 0x8B, green: 0x45, blue: 0x13},
'salmon': {red: 0xFA, green: 0x80, blue: 0x72},
'sandybrown': {red: 0xF4, green: 0xA4, blue: 0x60},
'seagreen': {red: 0x2E, green: 0x8B, blue: 0x57},
'seashell': {red: 0xFF, green: 0xF5, blue: 0xEE},
'sienna': {red: 0xA0, green: 0x52, blue: 0x2D},
'silver': {red: 0xC0, green: 0xC0, blue: 0xC0},
'skyblue': {red: 0x87, green: 0xCE, blue: 0xEB},
'slateblue': {red: 0x6A, green: 0x5A, blue: 0xCD},
'slategray': {red: 0x70, green: 0x80, blue: 0x90},
'slategrey': {red: 0x70, green: 0x80, blue: 0x90},
'snow': {red: 0xFF, green: 0xFA, blue: 0xFA},
'springgreen': {red: 0x00, green: 0xFF, blue: 0x7F},
'steelblue': {red: 0x46, green: 0x82, blue: 0xB4},
'tan': {red: 0xD2, green: 0xB4, blue: 0x8C},
'teal': {red: 0x00, green: 0x80, blue: 0x80},
'thistle': {red: 0xD8, green: 0xBF, blue: 0xD8},
'tomato': {red: 0xFF, green: 0x63, blue: 0x47},
'turquoise': {red: 0x40, green: 0xE0, blue: 0xD0},
'violet': {red: 0xEE, green: 0x82, blue: 0xEE},
'wheat': {red: 0xF5, green: 0xDE, blue: 0xB3},
'white': {red: 0xFF, green: 0xFF, blue: 0xFF},
'whitesmoke': {red: 0xF5, green: 0xF5, blue: 0xF5},
'yellow': {red: 0xFF, green: 0xFF, blue: 0x00},
'yellowgreen': {red: 0x9A, green: 0xCD, blue: 0x32},
'transparent': {red: 0x00, green: 0x00, blue: 0x00, alpha: 0.0}
};
/**
* Color class allows cross-browser comparison of values, which can
* be returned from queryCommandValue in several formats:
* #ff00ff
* #f0f
* rgb(255, 0, 0)
* rgb(100%, 0%, 28%) // disabled for the time being (see below)
* rgba(127, 0, 64, 0.25)
* rgba(50%, 0%, 10%, 0.65) // disabled for the time being (see below)
* palegoldenrod
* transparent
*
* @constructor
* @param value {String} original value
*/
function Color(value) {
this.compare = function(other) {
if (!this.valid || !other.valid) {
return false;
}
if (this.alpha != other.alpha) {
return false;
}
if (this.alpha == 0.0) {
// both are fully transparent -> ignore the specific color information
return true;
}
// TODO(rolandsteiner): handle hsl/hsla values
return this.red == other.red && this.green == other.green && this.blue == other.blue;
}
this.parse = function(value) {
if (!value)
return false;
value = String(value).toLowerCase();
var match;
// '#' + 6 hex digits, e.g., #ff3300
match = value.match(/#([0-9a-f]{6})/i);
if (match) {
this.red = parseInt(match[1].substring(0, 2), 16);
this.green = parseInt(match[1].substring(2, 4), 16);
this.blue = parseInt(match[1].substring(4, 6), 16);
this.alpha = 1.0;
return true;
}
// '#' + 3 hex digits, e.g., #f30
match = value.match(/#([0-9a-f]{3})/i);
if (match) {
this.red = parseInt(match[1].substring(0, 1), 16) * 16;
this.green = parseInt(match[1].substring(1, 2), 16) * 16;
this.blue = parseInt(match[1].substring(2, 3), 16) * 16;
this.alpha = 1.0;
return true;
}
// a color name, e.g., springgreen
match = colorChart[value];
if (match) {
this.red = match.red;
this.green = match.green;
this.blue = match.blue;
this.alpha = (match.alpha === undefined) ? 1.0 : match.alpha;
return true;
}
// rgb(r, g, b), e.g., rgb(128, 12, 217)
match = value.match(/rgb\(([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/i);
if (match) {
this.red = Number(match[1]);
this.green = Number(match[2]);
this.blue = Number(match[3]);
this.alpha = 1.0;
return true;
}
// rgb(r%, g%, b%), e.g., rgb(100%, 0%, 50%)
// Commented out for the time being, since it seems likely that the resulting
// decimal values will create false negatives when compared with non-% values.
//
// => store as separate percent values and do exact matching when compared with % values
// and fuzzy matching when compared with non-% values?
//
// match = value.match(/rgb\(([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*\)/i);
// if (match) {
// this.red = Number(match[1]) * 255 / 100;
// this.green = Number(match[2]) * 255 / 100;
// this.blue = Number(match[3]) * 255 / 100;
// this.alpha = 1.0;
// return true;
// }
// rgba(r, g, b, a), e.g., rgb(128, 12, 217, 0.2)
match = value.match(/rgba\(([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/i);
if (match) {
this.red = Number(match[1]);
this.green = Number(match[2]);
this.blue = Number(match[3]);
this.alpha = Number(match[4]);
return true;
}
// rgba(r%, g%, b%, a), e.g., rgb(100%, 0%, 50%, 0.3)
// Commented out for the time being (cf. rgb() matching above)
// match = value.match(/rgba\(([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%\s*,\s*([0-9]{0,3}(?:\.[0-9]+)?)%,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/i);
// if (match) {
// this.red = Number(match[1]) * 255 / 100;
// this.green = Number(match[2]) * 255 / 100;
// this.blue = Number(match[3]) * 255 / 100;
// this.alpha = Number(match[4]);
// return true;
// }
// TODO(rolandsteiner): handle "hsl(h, s, l)" and "hsla(h, s, l, a)" notation
return false;
}
this.toString = function() {
return this.valid ? this.red + ',' + this.green + ',' + this.blue : '(invalid)';
}
this.toHexString = function() {
if (!this.valid)
return '(invalid)';
return ((this.red < 16) ? '0' : '') + this.red.toString(16) +
((this.green < 16) ? '0' : '') + this.green.toString(16) +
((this.blue < 16) ? '0' : '') + this.blue.toString(16);
}
this.valid = this.parse(value);
}
/**
* Utility class for converting font sizes to the size
* attribute in a font tag. Currently only converts px because
* only the sizes and px ever come from queryCommandValue.
*
* @constructor
* @param value {String} original value
*/
function FontSize(value) {
this.parse = function(str) {
if (!str)
this.valid = false;
var match;
if (match = String(str).match(/([0-9]+)px/)) {
var px = Number(match[1]);
if (px <= 0 || px > 47)
return false;
if (px <= 10) {
this.size = '1';
} else if (px <= 13) {
this.size = '2';
} else if (px <= 16) {
this.size = '3';
} else if (px <= 18) {
this.size = '4';
} else if (px <= 24) {
this.size = '5';
} else if (px <= 32) {
this.size = '6';
} else {
this.size = '7';
}
return true;
}
if (match = String(str).match(/([+-][0-9]+)/)) {
this.size = match[1];
return this.size >= 1 && this.size <= 7;
}
if (Number(str)) {
this.size = String(Number(str));
return this.size >= 1 && this.size <= 7;
}
switch (str) {
case 'x-small':
this.size = '1';
return true;
case 'small':
this.size = '2';
return true;
case 'medium':
this.size = '3';
return true;
case 'large':
this.size = '4';
return true;
case 'x-large':
this.size = '5';
return true;
case 'xx-large':
this.size = '6';
return true;
case 'xxx-large':
this.size = '7';
return true;
case '-webkit-xxx-large':
this.size = '7';
return true;
case 'larger':
this.size = '+1';
return true;
case 'smaller':
this.size = '-1';
return true;
}
return false;
}
this.compare = function(other) {
return this.valid && other.valid && this.size === other.size;
}
this.toString = function() {
return this.valid ? this.size : '(invalid)';
}
this.valid = this.parse(value);
}
/**
* Utility class for converting & canonicalizing font names.
*
* @constructor
* @param value {String} original value
*/
function FontName(value) {
this.parse = function(str) {
if (!str)
return false;
str = String(str).toLowerCase();
switch (str) {
case 'arial new':
this.fontname = 'arial';
return true;
case 'courier new':
this.fontname = 'courier';
return true;
case 'times new':
case 'times roman':
case 'times new roman':
this.fontname = 'times';
return true;
}
this.fontname = value;
return true;
}
this.compare = function(other) {
return this.valid && other.valid && this.fontname === other.fontname;
}
this.toString = function() {
return this.valid ? this.fontname : '(invalid)';
}
this.valid = this.parse(value);
}

View File

@ -0,0 +1,227 @@
/**
* @fileoverview
* Common constants and variables used in the RTE test suite.
*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the 'License')
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @version 0.1
* @author rolandsteiner@google.com
*/
// Constant for indicating a test setup is unsupported or incorrect
// (threw exception).
var INTERNAL_ERR = 'INTERNAL ERROR: ';
var SETUP_EXCEPTION = 'SETUP EXCEPTION: ';
var EXECUTION_EXCEPTION = 'EXECUTION EXCEPTION: ';
var VERIFICATION_EXCEPTION = 'VERIFICATION EXCEPTION: ';
var SETUP_CONTAINER = 'WHEN INITIALIZING TEST CONTAINER';
var SETUP_BAD_SELECTION_SPEC = 'BAD SELECTION SPECIFICATION IN TEST OR EXPECTATION STRING';
var SETUP_HTML = 'WHEN SETTING TEST HTML';
var SETUP_SELECTION = 'WHEN SETTING SELECTION';
var SETUP_NOCOMMAND = 'NO COMMAND, GENERAL FUNCTION OR QUERY FUNCTION GIVEN';
var HTML_COMPARISON = 'WHEN COMPARING OUTPUT HTML';
// Exceptiona to be thrown on unsupported selection operations
var SELMODIFY_UNSUPPORTED = 'UNSUPPORTED selection.modify()';
var SELALLCHILDREN_UNSUPPORTED = 'UNSUPPORTED selection.selectAllChildren()';
// Output string for unsupported functions
// (returning bool 'false' as opposed to throwing an exception)
var UNSUPPORTED = '<i>false</i> (UNSUPPORTED)';
// HTML comparison result contants.
var VALRESULT_NOT_RUN = 0; // test hasn't been run yet
var VALRESULT_SETUP_EXCEPTION = 1;
var VALRESULT_EXECUTION_EXCEPTION = 2;
var VALRESULT_VERIFICATION_EXCEPTION = 3;
var VALRESULT_UNSUPPORTED = 4;
var VALRESULT_CANARY = 5; // HTML changes bled into the canary.
var VALRESULT_DIFF = 6;
var VALRESULT_ACCEPT = 7; // HTML technically correct, but not ideal.
var VALRESULT_EQUAL = 8;
var VALOUTPUT = [ // IMPORTANT: this array MUST be coordinated with the values above!!
{css: 'grey', output: '???', title: 'The test has not been run yet.'}, // VALRESULT_NOT_RUN
{css: 'exception', output: 'EXC.', title: 'Exception was thrown during setup.'}, // VALRESULT_SETUP_EXCEPTION
{css: 'exception', output: 'EXC.', title: 'Exception was thrown during execution.'}, // VALRESULT_EXECUTION_EXCEPTION
{css: 'exception', output: 'EXC.', title: 'Exception was thrown during result verification.'}, // VALRESULT_VERIFICATION_EXCEPTION
{css: 'unsupported', output: 'UNS.', title: 'Unsupported command or value'}, // VALRESULT_UNSUPPORTED
{css: 'canary', output: 'CANARY', title: 'The command affected the contentEditable root element, or outside HTML.'}, // VALRESULT_CANARY
{css: 'fail', output: 'FAIL', title: 'The result differs from the expectation(s).'}, // VALRESULT_DIFF
{css: 'accept', output: 'ACC.', title: 'The result is technically correct, but sub-optimal.'}, // VALRESULT_ACCEPT
{css: 'pass', output: 'PASS', title: 'The test result matches the expectation.'} // VALRESULT_EQUAL
]
// Selection comparison result contants.
var SELRESULT_NOT_RUN = 0; // test hasn't been run yet
var SELRESULT_CANARY = 1; // selection escapes the contentEditable element
var SELRESULT_DIFF = 2;
var SELRESULT_NA = 3;
var SELRESULT_ACCEPT = 4; // Selection is acceptable, but not ideal.
var SELRESULT_EQUAL = 5;
var SELOUTPUT = [ // IMPORTANT: this array MUST be coordinated with the values above!!
{css: 'grey', output: 'grey', title: 'The test has not been run yet.'}, // SELRESULT_NOT_RUN
{css: 'canary', output: 'CANARY', title: 'The selection escaped the contentEditable boundary!'}, // SELRESULT_CANARY
{css: 'fail', output: 'FAIL', title: 'The selection differs from the expectation(s).'}, // SELRESULT_DIFF
{css: 'na', output: 'N/A', title: 'The correctness of the selection could not be verified.'}, // SELRESULT_NA
{css: 'accept', output: 'ACC.', title: 'The selection is technically correct, but sub-optimal.'}, // SELRESULT_ACCEPT
{css: 'pass', output: 'PASS', title: 'The selection matches the expectation.'} // SELRESULT_EQUAL
];
// RegExp for selection markers
var SELECTION_MARKERS = /[\[\]\{\}\|\^]/;
// Special attributes used to mark selections within elements that otherwise
// have no children. Important: attribute name MUST be lower case!
var ATTRNAME_SEL_START = 'bsselstart';
var ATTRNAME_SEL_END = 'bsselend';
// DOM node type constants.
var DOM_NODE_TYPE_ELEMENT = 1;
var DOM_NODE_TYPE_TEXT = 3;
var DOM_NODE_TYPE_COMMENT = 8;
// Test parameter names
var PARAM_DESCRIPTION = 'desc';
var PARAM_PAD = 'pad';
var PARAM_EXECCOMMAND = 'command';
var PARAM_FUNCTION = 'function';
var PARAM_QUERYCOMMANDSUPPORTED = 'qcsupported';
var PARAM_QUERYCOMMANDENABLED = 'qcenabled';
var PARAM_QUERYCOMMANDINDETERM = 'qcindeterm';
var PARAM_QUERYCOMMANDSTATE = 'qcstate';
var PARAM_QUERYCOMMANDVALUE = 'qcvalue';
var PARAM_VALUE = 'value';
var PARAM_EXPECTED = 'expected';
var PARAM_EXPECTED_OUTER = 'expOuter';
var PARAM_ACCEPT = 'accept';
var PARAM_ACCEPT_OUTER = 'accOuter';
var PARAM_CHECK_ATTRIBUTES = 'checkAttrs';
var PARAM_CHECK_STYLE = 'checkStyle';
var PARAM_CHECK_CLASS = 'checkClass';
var PARAM_CHECK_ID = 'checkID';
var PARAM_STYLE_WITH_CSS = 'styleWithCSS';
// ID suffixes for the output columns
var IDOUT_TR = '_:TR:'; // per container
var IDOUT_TESTID = '_:tid'; // per test
var IDOUT_COMMAND = '_:cmd'; // per test
var IDOUT_VALUE = '_:val'; // per test
var IDOUT_CHECKATTRS = '_:att'; // per test
var IDOUT_CHECKSTYLE = '_:sty'; // per test
var IDOUT_CONTAINER = '_:cnt:'; // per container
var IDOUT_STATUSVAL = '_:sta:'; // per container
var IDOUT_STATUSSEL = '_:sel:'; // per container
var IDOUT_PAD = '_:pad'; // per test
var IDOUT_EXPECTED = '_:exp'; // per test
var IDOUT_ACTUAL = '_:act:'; // per container
// Output strings to use for yes/no/NA
var OUTSTR_YES = '&#x25CF;';
var OUTSTR_NO = '&#x25CB;';
var OUTSTR_NA = '-';
// Tags at the start of HTML strings where they were taken from
var HTMLTAG_BODY = 'B:';
var HTMLTAG_OUTER = 'O:';
var HTMLTAG_INNER = 'I:';
// What to use for the canary
var CANARY = 'CAN<br>ARY';
// Containers for tests, and their associated DOM elements:
// iframe, win, doc, body, elem
var containers = [
{ id: 'dM',
iframe: null,
win: null,
doc: null,
body: null,
editor: null,
tagOpen: '<body>',
tagClose: '</body>',
editorID: null,
canary: '',
},
{ id: 'body',
iframe: null,
win: null,
doc: null,
body: null,
editor: null,
tagOpen: '<body contenteditable="true">',
tagClose: '</body>',
editorID: null,
canary: ''
},
{ id: 'div',
iframe: null,
win: null,
doc: null,
body: null,
editor: null,
tagOpen: '<div contenteditable="true" id="editor-div">',
tagClose: '</div>',
editorID: 'editor-div',
canary: CANARY
}
];
// Helper variables to use in test functions
var win = null; // window object to use for test functions
var doc = null; // document object to use for test functions
var body = null; // The <body> element of the current document
var editor = null; // The contentEditable element (i.e., the <body> or <div>)
var sel = null; // The current selection after the pad is set up
// Canonicalization emit flags for various purposes
var emitFlagsForCanary = {
emitAttrs: true,
emitStyle: true,
emitClass: true,
emitID: true,
lowercase: true,
canonicalizeUnits: true
};
var emitFlagsForOutput = {
emitAttrs: true,
emitStyle: true,
emitClass: true,
emitID: true,
lowercase: false,
canonicalizeUnits: false
};
// Shades of output colors
var colorShades = ['Lo', 'Hi'];
// Classes of tests
var testClassIDs = ['Finalized', 'RFC', 'Proposed'];
var testClassCount = testClassIDs.length;
// Dictionary storing the detailed test results.
var results = {
count: 0,
score: 0
};
// Results - populated by the fillResults() function.
var beacon = [];
// "compatibility" between Python and JS for test quines
var True = true;
var False = false;

View File

@ -0,0 +1,138 @@
<!-- Legend -->
<TABLE CLASS="legend framed">
<THEAD>
<TR><TH COLSPAN=3 CLASS="legendHdr">Result Description</TH></TR>
<TR><TH>Status</TH><TH ALIGN="LEFT">Meaning</TH><TH ALIGN="LEFT">Explanation</TH><TH>Scoring</TH></TR>
</THEAD>
<TBODY>
<TR CLASS="lo"><TD CLASS="pass" ALIGN="CENTER">&nbsp;PASS&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Passed</TD><TD CLASS="legend" ROWSPAN=2>The result matches the expectation.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="pass">PASS (+1)</TD></TR>
<TR CLASS="hi"><TD CLASS="pass" ALIGN="CENTER">&nbsp;PASS&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="accept" ALIGN="CENTER">&nbsp;ACC.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Acceptable</TD><TD CLASS="legend" ROWSPAN=2>The result is technically correct, but not ideal (too verbose, deprecated usage, etc.) - for informative purposes only.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="accept" ALIGN="CENTER">&nbsp;ACC.&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="fail" ALIGN="CENTER">&nbsp;FAIL&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Failure</TD><TD CLASS="legend" ROWSPAN=2>The result does not match any given expectation.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="fail" ALIGN="CENTER">&nbsp;FAIL&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="canary" ALIGN="CENTER">&nbsp;CANARY&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Canary</TD><TD CLASS="legend" ROWSPAN=2>The result changes HTML other than children of the contentEditable element.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="canary" ALIGN="CENTER">&nbsp;CANARY&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="unsupported" ALIGN="CENTER">&nbsp;UNS.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Unsupported</TD><TD CLASS="legend" ROWSPAN=2>The specific function or value is unsupported (returned boolean 'false').</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="unsupported" ALIGN="CENTER">&nbsp;UNS.&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="exception" ALIGN="CENTER">&nbsp;EXC.&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Exception</TD><TD CLASS="legend" ROWSPAN=2>An unexpected exception was thrown during the execution of the test.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="exception" ALIGN="CENTER">&nbsp;EXC.&nbsp;</TD></TR>
<TR CLASS="lo"><TD CLASS="na" ALIGN="CENTER">&nbsp;N/A&nbsp;</TD><TD CLASS="legend" ROWSPAN=2>Not Applicable</TD><TD CLASS="legend" ROWSPAN=2>The selection could not be tested, because the tested function failed to return a known result.</TD><TD ROWSPAN=2 ALIGN="CENTER" CLASS="fail">FAIL (+0)</TD></TR>
<TR CLASS="hi"><TD CLASS="na" ALIGN="CENTER">&nbsp;N/A&nbsp;</TD></TR>
</TBODY>
</TABLE>
<TABLE CLASS="legend framed">
<THEAD>
<TR><TH COLSPAN=2 CLASS="legendHdr">Selection and Result Display</TH></TR>
<TR><TH>Character</TH><TH ALIGN="LEFT">Explanation</TH></TR>
</THEAD>
<TBODY>
<TR><TD CLASS="sel" ALIGN="CENTER">[</TD><TD>Start of selection - selection point is within a text node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">]</TD><TD>End of selection - selection point is within a text node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">^</TD><TD>Collapsed selection - selection point is within a text node.</TD></TR>
<TR><TD COLSPAN=2>&nbsp;</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">{</TD><TD>Start of selection - selection point is within an element node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">}</TD><TD>End of selection - selection point is within an element node.</TD></TR>
<TR><TD CLASS="sel" ALIGN="CENTER">|</TD><TD>Collapsed selection - selection point is within an element node.</TD></TR>
<TR><TD COLSPAN=2>&nbsp;</TD></TR>
<TR><TD ALIGN="CENTER"><SPAN CLASS="fade">foo</SPAN></TD><TD>Greyed text indicates parts of the output that are ignored for the purposes of checking the result.</TD></TR>
<TR><TD ALIGN="CENTER"><SPAN CLASS="txt">foo</SPAN></TD><TD>Grey border indicates extent of text nodes in the result.</TD></TR>
</TBODY>
</TABLE>
<!-- progress meter -->
<HR ID="divider">
<H1>Running Test Suites: {% for s in suites %}<A HREF="#{{ s.id }}" ID="{{ s.id }}-progress" STYLE="color: #eeeeee">{{ s.id }}</A> {% endfor %}<SPAN ID="done">&nbsp;</SPAN></H1>
<HR>
<!-- main output -->
{% for s in suites %}
<H1 ID="{{ s.id }}"><A NAME="{{ s.id }}" HREF="#{{ s.id }}">{{ s.id }}</A> - {{ s.caption }}:
<SPAN ID="{{ s.id }}-{% ifequal s.id.0 'S' %}sel{% endifequal %}score">?/?</SPAN>
{% ifnotequal s.id.0 "Q" %}{% ifnotequal s.id.0 "S" %}
(Selection: <SPAN ID="{{ s.id }}-selscore">?/?</SPAN>)
{% endifnotequal %}{% endifnotequal %}
(time: <SPAN ID="{{ s.id }}-time">?</SPAN>&nbsp;ms)
</H1>
{% if s.comment %}
<DIV CLASS="comment">{{ s.comment|safe }}</DIV>
{% endif %}
{% for cls in classes %}{% for pk, pv in s.items %}{% ifequal pk cls %}
<H2 ID="{{ s.id }}-{{ cls }}"><A NAME="{{ s.id }}-{{ cls }}" HREF="#{{ s.id }}-{{ cls }}">{{ cls }} Tests</A>:
<SPAN ID="{{ s.id }}-{{ cls }}-{% ifequal s.id.0 'S' %}sel{% endifequal %}score">?/?</SPAN>
{% ifnotequal s.id.0 "Q" %}{% ifnotequal s.id.0 "S" %}
(Selection: <SPAN ID="{{ s.id }}-{{ cls }}-selscore">?/?</SPAN>)
{% endifnotequal %}{% endifnotequal %}
</H2>
<TABLE WIDTH=100%>
<THEAD>
<TR>
<TH TITLE="Unique ID of the test" ALIGN="LEFT">ID</TH>
<TH TITLE="Command or function used in the test" ALIGN="LEFT">Command</TH>
<TH TITLE="Value field for commands" ALIGN="LEFT">Value</TH>
{% ifnotequal s.id.0 "S" %}{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output attribute and style columns for selection and "queryCommand..." tests. {% endcomment %}
<TH TITLE="check Atributes?">A</TH>
<TH TITLE="check Style">S</TH>
{% endifnotequal %}{% endifnotequal %}
<TH TITLE="Testing HTML Element">Env.</TH>
{% ifnotequal s.id.0 "S" %}{% comment %} Don't output HTML status column for selection tests. {% endcomment %}
<TH TITLE="State of the test">Status</TH>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output selection result column for "queryCommand..." tests. {% endcomment %}
<TH TITLE="State of the test regarding the selection">Selection</TH>
{% endifnotequal %}
<TH TITLE="Initial HTML and selection" ALIGN="LEFT">Initial</TH>
<TH TITLE="Expected HTML and selection" ALIGN="LEFT">Expected</TH>
<TH TITLE="Actual result HTML and selection" ALIGN="LEFT">Actual (lower case, canonicalized, selection marks)</TH>
<TH TITLE="Short description of the test" ALIGN="LEFT">Description</TH>
</TR>
</THEAD>
<TBODY>
{% for g in pv %}{% for t in g.tests %}
<TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:dM" CLASS="{% cycle 'lo' 'lo' 'lo' 'hi' 'hi' 'hi' as shade %}">
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:tid"><A CLASS="idLabel" NAME="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}" HREF="#{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}">{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}</A></TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cmd">&nbsp;</TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:val">&nbsp;</TD>
{% ifnotequal s.id.0 "S" %}{% ifnotequal s.id.0 "Q" %}{% comment %} Don't output attribute and style columns for selection and "queryCommand..." tests. {% endcomment %}
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:att" ALIGN="CENTER">&nbsp;</TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sty" ALIGN="CENTER">&nbsp;</TD>
{% endifnotequal %}{% endifnotequal %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:dM" TITLE="designMode=&quot;on&quot;" ALIGN="CENTER">dM</TD>
{% ifnotequal s.id.0 "S" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:dM" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:dM" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:pad">&nbsp;</TD>
<TD ROWSPAN=3 ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:exp">&nbsp;</TD>
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:dM"><I>Processing...</I></TD>
<TD ROWSPAN=3>{{ t.desc|default:"&nbsp;" }}</TD>
</TR>
<TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:body" CLASS="{% cycle shade %}">
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:body" TITLE="&lt;body contentEditable=&quot;true&quot;&gt;" ALIGN="CENTER">body</TD>
{% ifnotequal s.id.0 "S" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:body" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:body" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:body"><I>Processing...</I></TD>
</TR>
<TR ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:TR:div" CLASS="{% cycle shade %}">
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:cnt:div" TITLE="&lt;div contentEditable=&quot;true&quot;&gt;" ALIGN="CENTER">div</TD>
{% ifnotequal s.id.0 "S" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sta:div" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
{% ifnotequal s.id.0 "Q" %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:sel:div" ALIGN="CENTER">NONE</TD>
{% endifnotequal %}
<TD ID="{{ commonIDPrefix }}-{{ s.id }}_{{ t.id }}_:act:div"><I>Processing...</I></TD>
</TR>
{% endfor %}{% endfor %}
</TBODY>
</TABLE>
{% endifequal %}{% endfor %}{% endfor %}
{% endfor %}

View File

@ -0,0 +1,107 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>New Rich Text Tests</title>
<link rel="stylesheet" href="static/common.css" type="text/css">
<link rel="stylesheet" href="static/editable.css" type="text/css">
<!-- utility scripts -->
<script src="static/js/variables.js"></script>
<script src="static/js/canonicalize.js"></script>
<script src="static/js/compare.js"></script>
<script src="static/js/output.js"></script>
<script src="static/js/pad.js"></script>
<script src="static/js/range.js"></script>
<script src="static/js/units.js"></script>
<script src="static/js/run.js"></script>
<!-- new tests -->
<script type="text/javascript">
{% autoescape off %}
var commonIDPrefix = '{{ commonIDPrefix }}';
{% for s in suites %}
var {{ s.id }}_TESTS = {{ s }};
{% endfor %}
/**
* Stuff to do after all tests are run:
* - write a nice "DONE!" at the end of the progress meter
* - beacon the results
* - remove the testing <iframe>s
*/
function finish() {
var span = document.getElementById('done');
if (span)
span.innerHTML = ' ... DONE!';
fillResults();
parent.sendScore(beacon, categoryTotals);
cleanUp();
}
/**
* Run every individual suite, with a a brief timeout in between
* to allow for screen updates.
*/
{% for s in suites %}
{% if not forloop.first %}
setTimeout("runSuite{{ s.id }}()", 100);
}
{% endif %}
function runSuite{{ s.id }}() {
runAndOutputTestSuite({{ s.id }}_TESTS);
{% endfor %}
finish();
}
/**
* Runs all tests in all suites.
*/
function doRunTests() {
initVariables();
initEditorDocs();
// Start with the first test suite
runSuite{{ suites.0.id }}();
}
/**
* Runs after allowing for some time to have everything loaded
* (aka. horrible IE9 kludge)
*/
function runTests() {
setTimeout("doRunTests()", 1500);
}
/**
* Removes the <iframe>s after all tests are finished
*/
function cleanUp() {
var e = document.getElementById('iframe-dM');
e.parentNode.removeChild(e);
e = document.getElementById('iframe-body');
e.parentNode.removeChild(e);
e = document.getElementById('iframe-div');
e.parentNode.removeChild(e);
}
{% endautoescape %}
</script>
</head>
<body onload="runTests()">
{% include "richtext2/templates/output.html" %}
<hr>
<iframe name="iframe-dM" id="iframe-dM" src="static/editable-dM.html"></iframe>
<iframe name="iframe-body" id="iframe-body" src="static/editable-body.html"></iframe>
<iframe name="iframe-div" id="iframe-div" src="static/editable-div.html"></iframe>
</body>
</html>

View File

@ -0,0 +1,17 @@
__all__ = [
'apply',
'applyCSS',
'change',
'changeCSS',
'delete',
'forwarddelete',
'insert',
'queryEnabled',
'queryIndeterm',
'queryState',
'querySupported',
'queryValue',
'selection',
'unapply',
'unapplyCSS'
]

View File

@ -0,0 +1,364 @@
APPLY_TESTS = {
'id': 'A',
'caption': 'Apply Formatting Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] bold',
'command': 'bold',
'tests': [
{ 'id': 'B_TEXT-1_SI',
'rte1-id': 'a-bold-0',
'desc': 'Bold selection',
'pad': 'foo[bar]baz',
'expected': [ 'foo<b>[bar]</b>baz',
'foo<strong>[bar]</strong>baz' ] },
{ 'id': 'B_TEXT-1_SIR',
'desc': 'Bold reversed selection',
'pad': 'foo]bar[baz',
'expected': [ 'foo<b>[bar]</b>baz',
'foo<strong>[bar]</strong>baz' ] },
{ 'id': 'B_I-1_SL',
'desc': 'Bold selection, partially including italic',
'pad': 'foo[bar<i>baz]qoz</i>quz',
'expected': [ 'foo<b>[bar</b><i><b>baz]</b>qoz</i>quz',
'foo<b>[bar<i>baz]</i></b><i>qoz</i>quz',
'foo<strong>[bar</strong><i><strong>baz]</strong>qoz</i>quz',
'foo<strong>[bar<i>baz]</i></strong><i>qoz</i>quz' ] }
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_TEXT-1_SI',
'rte1-id': 'a-italic-0',
'desc': 'Italicize selection',
'pad': 'foo[bar]baz',
'expected': [ 'foo<i>[bar]</i>baz',
'foo<em>[bar]</em>baz' ] }
]
},
{ 'desc': '[HTML5] underline',
'command': 'underline',
'tests': [
{ 'id': 'U_TEXT-1_SI',
'rte1-id': 'a-underline-0',
'desc': 'Underline selection',
'pad': 'foo[bar]baz',
'expected': 'foo<u>[bar]</u>baz' }
]
},
{ 'desc': '[HTML5] strikethrough',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_TEXT-1_SI',
'rte1-id': 'a-strikethrough-0',
'desc': 'Strike-through selection',
'pad': 'foo[bar]baz',
'expected': [ 'foo<s>[bar]</s>baz',
'foo<strike>[bar]</strike>baz',
'foo<del>[bar]</del>baz' ] }
]
},
{ 'desc': '[HTML5] subscript',
'command': 'subscript',
'tests': [
{ 'id': 'SUB_TEXT-1_SI',
'rte1-id': 'a-subscript-0',
'desc': 'Change selection to subscript',
'pad': 'foo[bar]baz',
'expected': 'foo<sub>[bar]</sub>baz' }
]
},
{ 'desc': '[HTML5] superscript',
'command': 'superscript',
'tests': [
{ 'id': 'SUP_TEXT-1_SI',
'rte1-id': 'a-superscript-0',
'desc': 'Change selection to superscript',
'pad': 'foo[bar]baz',
'expected': 'foo<sup>[bar]</sup>baz' }
]
},
{ 'desc': '[HTML5] createlink',
'command': 'createlink',
'tests': [
{ 'id': 'CL:url_TEXT-1_SI',
'rte1-id': 'a-createlink-0',
'desc': 'create a link around the selection',
'value': '#foo',
'pad': 'foo[bar]baz',
'expected': 'foo<a href="#foo">[bar]</a>baz' }
]
},
{ 'desc': '[HTML5] formatBlock',
'command': 'formatblock',
'tests': [
{ 'id': 'FB:H1_TEXT-1_SI',
'rte1-id': 'a-formatblock-0',
'desc': 'format the selection into a block: use <h1>',
'value': 'h1',
'pad': 'foo[bar]baz',
'expected': '<h1>foo[bar]baz</h1>' },
{ 'id': 'FB:P_TEXT-1_SI',
'desc': 'format the selection into a block: use <p>',
'value': 'p',
'pad': 'foo[bar]baz',
'expected': '<p>foo[bar]baz</p>' },
{ 'id': 'FB:PRE_TEXT-1_SI',
'desc': 'format the selection into a block: use <pre>',
'value': 'pre',
'pad': 'foo[bar]baz',
'expected': '<pre>foo[bar]baz</pre>' },
{ 'id': 'FB:ADDRESS_TEXT-1_SI',
'desc': 'format the selection into a block: use <address>',
'value': 'address',
'pad': 'foo[bar]baz',
'expected': '<address>foo[bar]baz</address>' },
{ 'id': 'FB:BQ_TEXT-1_SI',
'desc': 'format the selection into a block: use <blockquote>',
'value': 'blockquote',
'pad': 'foo[bar]baz',
'expected': '<blockquote>foo[bar]baz</blockquote>' },
{ 'id': 'FB:BQ_BR.BR-1_SM',
'desc': 'format a multi-line selection into a block: use <blockquote>',
'command': 'formatblock',
'value': 'blockquote',
'pad': 'fo[o<br>bar<br>b]az',
'expected': '<blockquote>fo[o<br>bar<br>b]az</blockquote>' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:blue_TEXT-1_SI',
'rte1-id': 'a-backcolor-0',
'desc': 'Change background color (note: no non-CSS variant available)',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] forecolor',
'command': 'forecolor',
'tests': [
{ 'id': 'FC:blue_TEXT-1_SI',
'rte1-id': 'a-forecolor-0',
'desc': 'Change the text color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': 'foo<font color="blue">[bar]</font>baz' }
]
},
{ 'desc': '[MIDAS] hilitecolor',
'command': 'hilitecolor',
'tests': [
{ 'id': 'HC:blue_TEXT-1_SI',
'rte1-id': 'a-hilitecolor-0',
'desc': 'Change the hilite color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:a_TEXT-1_SI',
'rte1-id': 'a-fontname-0',
'desc': 'Change the font name',
'value': 'arial',
'pad': 'foo[bar]baz',
'expected': 'foo<font face="arial">[bar]</font>baz' }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:2_TEXT-1_SI',
'rte1-id': 'a-fontsize-0',
'desc': 'Change the font size to "2"',
'value': '2',
'pad': 'foo[bar]baz',
'expected': 'foo<font size="2">[bar]</font>baz' },
{ 'id': 'FS:18px_TEXT-1_SI',
'desc': 'Change the font size to "18px"',
'value': '18px',
'pad': 'foo[bar]baz',
'expected': 'foo<font size="18px">[bar]</font>baz' },
{ 'id': 'FS:large_TEXT-1_SI',
'desc': 'Change the font size to "large"',
'value': 'large',
'pad': 'foo[bar]baz',
'expected': 'foo<font size="large">[bar]</font>baz' }
]
},
{ 'desc': '[MIDAS] increasefontsize',
'command': 'increasefontsize',
'tests': [
{ 'id': 'INCFS:2_TEXT-1_SI',
'desc': 'Decrease the font size (to small)',
'pad': 'foo[bar]baz',
'expected': [ 'foo<font size="4">[bar]</font>baz',
'foo<font size="+1">[bar]</font>baz',
'foo<big>[bar]</big>baz' ] }
]
},
{ 'desc': '[MIDAS] decreasefontsize',
'command': 'decreasefontsize',
'tests': [
{ 'id': 'DECFS:2_TEXT-1_SI',
'rte1-id': 'a-decreasefontsize-0',
'desc': 'Decrease the font size (to small)',
'pad': 'foo[bar]baz',
'expected': [ 'foo<font size="2">[bar]</font>baz',
'foo<font size="-1">[bar]</font>baz',
'foo<small>[bar]</small>baz' ] }
]
},
{ 'desc': '[MIDAS] indent (note: accept the de-facto standard indent of 40px)',
'command': 'indent',
'tests': [
{ 'id': 'IND_TEXT-1_SI',
'rte1-id': 'a-indent-0',
'desc': 'Indent the text (accept the de-facto standard of 40px indent)',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'expected': [ '<blockquote>foo[bar]baz</blockquote>',
'<div style="margin-left: 40px">foo[bar]baz</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="margin-left: 40px">foo[bar]baz</div>' } }
]
},
{ 'desc': '[MIDAS] outdent (-> unapply tests)',
'command': 'outdent',
'tests': [
]
},
{ 'desc': '[MIDAS] justifycenter',
'command': 'justifycenter',
'tests': [
{ 'id': 'JC_TEXT-1_SC',
'rte1-id': 'a-justifycenter-0',
'desc': 'justify the text centrally',
'pad': 'foo^bar',
'expected': [ '<center>foo^bar</center>',
'<p align="center">foo^bar</p>',
'<p align="middle">foo^bar</p>',
'<div align="center">foo^bar</div>',
'<div align="middle">foo^bar</div>' ],
'div': {
'accOuter': [ '<div align="center" contenteditable="true">foo^bar</div>',
'<div align="middle" contenteditable="true">foo^bar</div>' ] } }
]
},
{ 'desc': '[MIDAS] justifyfull',
'command': 'justifyfull',
'tests': [
{ 'id': 'JF_TEXT-1_SC',
'rte1-id': 'a-justifyfull-0',
'desc': 'justify the text fully',
'pad': 'foo^bar',
'expected': [ '<p align="justify">foo^bar</p>',
'<div align="justify">foo^bar</div>' ],
'div': {
'accOuter': '<div align="justify" contenteditable="true">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyleft',
'command': 'justifyleft',
'tests': [
{ 'id': 'JL_TEXT-1_SC',
'rte1-id': 'a-justifyleft-0',
'desc': 'justify the text left',
'pad': 'foo^bar',
'expected': [ '<p align="left">foo^bar</p>',
'<div align="left">foo^bar</div>' ],
'div': {
'accOuter': '<div align="left" contenteditable="true">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyright',
'command': 'justifyright',
'tests': [
{ 'id': 'JR_TEXT-1_SC',
'rte1-id': 'a-justifyright-0',
'desc': 'justify the text right',
'pad': 'foo^bar',
'expected': [ '<p align="right">foo^bar</p>',
'<div align="right">foo^bar</div>' ],
'div': {
'accOuter': '<div align="right" contenteditable="true">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] heading',
'command': 'heading',
'tests': [
{ 'id': 'H:H1_TEXT-1_SC',
'desc': 'create a heading from the paragraph that contains the selection',
'value': 'h1',
'pad': 'foo[bar]baz',
'expected': '<h1>foo[bar]baz</h1>' }
]
},
{ 'desc': '[Other] createbookmark',
'command': 'createbookmark',
'tests': [
{ 'id': 'CB:name_TEXT-1_SI',
'rte1-id': 'a-createbookmark-0',
'desc': 'create a bookmark (named link) around selection',
'value': 'created',
'pad': 'foo[bar]baz',
'expected': 'foo<a name="created">[bar]</a>baz' }
]
}
]
}

View File

@ -0,0 +1,244 @@
APPLY_TESTS_CSS = {
'id': 'AC',
'caption': 'Apply Formatting Tests, using styleWithCSS',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': True,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] bold',
'command': 'bold',
'tests': [
{ 'id': 'B_TEXT-1_SI',
'rte1-id': 'a-bold-1',
'desc': 'Bold selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="font-weight: bold">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_TEXT-1_SI',
'rte1-id': 'a-italic-1',
'desc': 'Italicize selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="font-style: italic">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] underline',
'command': 'underline',
'tests': [
{ 'id': 'U_TEXT-1_SI',
'rte1-id': 'a-underline-1',
'desc': 'Underline selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="text-decoration: underline">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] strikethrough',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_TEXT-1_SI',
'rte1-id': 'a-strikethrough-1',
'desc': 'Strike-through selection',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="text-decoration: line-through">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] subscript',
'command': 'subscript',
'tests': [
{ 'id': 'SUB_TEXT-1_SI',
'rte1-id': 'a-subscript-1',
'desc': 'Change selection to subscript',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="vertical-align: sub">[bar]</span>baz' }
]
},
{ 'desc': '[HTML5] superscript',
'command': 'superscript',
'tests': [
{ 'id': 'SUP_TEXT-1_SI',
'rte1-id': 'a-superscript-1',
'desc': 'Change selection to superscript',
'pad': 'foo[bar]baz',
'expected': 'foo<span style="vertical-align: super">[bar]</span>baz' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:blue_TEXT-1_SI',
'rte1-id': 'a-backcolor-1',
'desc': 'Change background color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] forecolor',
'command': 'forecolor',
'tests': [
{ 'id': 'FC:blue_TEXT-1_SI',
'rte1-id': 'a-forecolor-1',
'desc': 'Change the text color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="color: blue">[bar]</span>baz',
'foo<font style="color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] hilitecolor',
'command': 'hilitecolor',
'tests': [
{ 'id': 'HC:blue_TEXT-1_SI',
'rte1-id': 'a-hilitecolor-1',
'desc': 'Change the hilite color',
'value': 'blue',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="background-color: blue">[bar]</span>baz',
'foo<font style="background-color: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:a_TEXT-1_SI',
'rte1-id': 'a-fontname-1',
'desc': 'Change the font name',
'value': 'arial',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-family: arial">[bar]</span>baz',
'foo<font style="font-family: blue">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:2_TEXT-1_SI',
'rte1-id': 'a-fontsize-1',
'desc': 'Change the font size to "2"',
'value': '2',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-size: small">[bar]</span>baz',
'foo<font style="font-size: small">[bar]</font>baz' ] },
{ 'id': 'FS:18px_TEXT-1_SI',
'desc': 'Change the font size to "18px"',
'value': '18px',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-size: 18px">[bar]</span>baz',
'foo<font style="font-size: 18px">[bar]</font>baz' ] },
{ 'id': 'FS:large_TEXT-1_SI',
'desc': 'Change the font size to "large"',
'value': 'large',
'pad': 'foo[bar]baz',
'expected': [ 'foo<span style="font-size: large">[bar]</span>baz',
'foo<font style="font-size: large">[bar]</font>baz' ] }
]
},
{ 'desc': '[MIDAS] indent',
'command': 'indent',
'tests': [
{ 'id': 'IND_TEXT-1_SI',
'rte1-id': 'a-indent-1',
'desc': 'Indent the text (assume "standard" 40px)',
'pad': 'foo[bar]baz',
'expected': [ '<div style="margin-left: 40px">foo[bar]baz</div>',
'<div style="margin: 0 0 0 40px">foo[bar]baz</div>',
'<blockquote style="margin-left: 40px">foo[bar]baz</blockquote>',
'<blockquote style="margin: 0 0 0 40px">foo[bar]baz</blockquote>' ],
'div': {
'accOuter': [ '<div contenteditable="true" style="margin-left: 40px">foo[bar]baz</div>',
'<div contenteditable="true" style="margin: 0 0 0 40px">foo[bar]baz</div>' ] } }
]
},
{ 'desc': '[MIDAS] outdent (-> unapply tests)',
'command': 'outdent',
'tests': [
]
},
{ 'desc': '[MIDAS] justifycenter',
'command': 'justifycenter',
'tests': [
{ 'id': 'JC_TEXT-1_SC',
'rte1-id': 'a-justifycenter-1',
'desc': 'justify the text centrally',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: center">foo^bar</p>',
'<div style="text-align: center">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: center">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyfull',
'command': 'justifyfull',
'tests': [
{ 'id': 'JF_TEXT-1_SC',
'rte1-id': 'a-justifyfull-1',
'desc': 'justify the text fully',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: justify">foo^bar</p>',
'<div style="text-align: justify">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: justify">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyleft',
'command': 'justifyleft',
'tests': [
{ 'id': 'JL_TEXT-1_SC',
'rte1-id': 'a-justifyleft-1',
'desc': 'justify the text left',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: left">foo^bar</p>',
'<div style="text-align: left">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: left">foo^bar</div>' } }
]
},
{ 'desc': '[MIDAS] justifyright',
'command': 'justifyright',
'tests': [
{ 'id': 'JR_TEXT-1_SC',
'rte1-id': 'a-justifyright-1',
'desc': 'justify the text right',
'pad': 'foo^bar',
'expected': [ '<p style="text-align: right">foo^bar</p>',
'<div style="text-align: right">foo^bar</div>' ],
'div': {
'accOuter': '<div contenteditable="true" style="text-align: right">foo^bar</div>' } }
]
}
]
}

View File

@ -0,0 +1,273 @@
CHANGE_TESTS = {
'id': 'C',
'caption': 'Change Existing Format to Different Format Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SL',
'desc': 'Italicize partially italicized text',
'pad': 'foo[bar<i>baz]</i>qoz',
'expected': 'foo<i>[barbaz]</i>qoz' },
{ 'id': 'I_B-I-1_SO',
'desc': 'Italicize partially italicized text in bold context',
'pad': '<b>foo[bar<i>baz</i>}</b>',
'expected': '<b>foo<i>[barbaz]</i></b>' }
]
},
{ 'desc': '[HTML5] underline',
'command': 'underline',
'tests': [
{ 'id': 'U_U-1_SO',
'desc': 'Underline partially underlined text',
'pad': 'foo[bar<u>baz</u>qoz]quz',
'expected': 'foo<u>[barbazqoz]</u>quz' },
{ 'id': 'U_U-1_SL',
'desc': 'Underline partially underlined text',
'pad': 'foo[bar<u>baz]qoz</u>quz',
'expected': 'foo<u>[barbaz]qoz</u>quz' },
{ 'id': 'U_S-U-1_SO',
'desc': 'Underline partially underlined text in striked context',
'pad': '<s>foo[bar<u>baz</u>}</s>',
'expected': '<s>foo<u>[barbaz]</u></s>' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:842_FONTs:bc:fca-1_SW',
'rte1-id': 'c-backcolor-0',
'desc': 'Change background color to new color',
'value': '#884422',
'pad': '<font style="background-color: #ffccaa">[foobarbaz]</font>',
'expected': [ '<font style="background-color: #884422">[foobarbaz]</font>',
'<span style="background-color: #884422">[foobarbaz]</span>' ] },
{ 'id': 'BC:00f_SPANs:bc:f00-1_SW',
'rte1-id': 'c-backcolor-2',
'desc': 'Change background color to new color',
'value': '#0000ff',
'pad': '<span style="background-color: #ff0000">[foobarbaz]</span>',
'expected': [ '<font style="background-color: #0000ff">[foobarbaz]</font>',
'<span style="background-color: #0000ff">[foobarbaz]</span>' ] },
{ 'id': 'BC:ace_FONT.ass.s:bc:rgb-1_SW',
'rte1-id': 'c-backcolor-1',
'desc': 'Change background color in styled span to new color',
'value': '#aaccee',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">[foobarbaz]</span>',
'expected': [ '<font style="background-color: #aaccee">[foobarbaz]</font>',
'<span style="background-color: #aaccee">[foobarbaz]</span>' ] }
]
},
{ 'desc': '[MIDAS] forecolor',
'command': 'forecolor',
'tests': [
{ 'id': 'FC:g_FONTc:b-1_SW',
'rte1-id': 'c-forecolor-0',
'desc': 'Change the text color (without CSS)',
'value': 'green',
'pad': '<font color="blue">[foobarbaz]</font>',
'expected': '<font color="green">[foobarbaz]</font>' },
{ 'id': 'FC:g_SPANs:c:g-1_SW',
'rte1-id': 'c-forecolor-1',
'desc': 'Change the text color from a styled span (without CSS)',
'value': 'green',
'pad': '<span style="color: blue">[foobarbaz]</span>',
'expected': '<font color="green">[foobarbaz]</font>' },
{ 'id': 'FC:g_FONTc:b.s:c:r-1_SW',
'rte1-id': 'c-forecolor-2',
'desc': 'Change the text color from conflicting color and style (without CSS)',
'value': 'green',
'pad': '<font color="blue" style="color: red">[foobarbaz]</font>',
'expected': '<font color="green">[foobarbaz]</font>' },
{ 'id': 'FC:g_FONTc:b.sz:6-1_SI',
'desc': 'Change the font color in content with a different font size and font color',
'value': 'green',
'pad': '<font color="blue" size="6">foo[bar]baz</font>',
'expected': [ '<font color="blue" size="6">foo<font color="green">[bar]</font>baz</font>',
'<font size="6"><font color="blue">foo<font color="green">[bar]</font><font color="blue">baz</font></font>' ] }
]
},
{ 'desc': '[MIDAS] hilitecolor',
'command': 'hilitecolor',
'tests': [
{ 'id': 'HC:g_FONTs:c:b-1_SW',
'rte1-id': 'c-hilitecolor-0',
'desc': 'Change the hilite color (without CSS)',
'value': 'green',
'pad': '<font style="background-color: blue">[foobarbaz]</font>',
'expected': [ '<font style="background-color: green">[foobarbaz]</font>',
'<span style="background-color: green">[foobarbaz]</span>' ] },
{ 'id': 'HC:g_SPANs:c:g-1_SW',
'rte1-id': 'c-hilitecolor-2',
'desc': 'Change the hilite color from a styled span (without CSS)',
'value': 'green',
'pad': '<span style="background-color: blue">[foobarbaz]</span>',
'expected': '<span style="background-color: green">[foobarbaz]</span>' },
{ 'id': 'HC:g_SPAN.ass.s:c:rgb-1_SW',
'rte1-id': 'c-hilitecolor-1',
'desc': 'Change the hilite color from a styled span (without CSS)',
'value': 'green',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0);">[foobarbaz]</span>',
'expected': '<span style="background-color: green">[foobarbaz]</span>' }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:c_FONTf:a-1_SW',
'rte1-id': 'c-fontname-0',
'desc': 'Change existing font name to new font name (without CSS)',
'value': 'courier',
'pad': '<font face="arial">[foobarbaz]</font>',
'expected': '<font face="courier">[foobarbaz]</font>' },
{ 'id': 'FN:c_SPANs:ff:a-1_SW',
'rte1-id': 'c-fontname-1',
'desc': 'Change existing font name from style to new font name (without CSS)',
'value': 'courier',
'pad': '<span style="font-family: arial">[foobarbaz]</span>',
'expected': '<font face="courier">[foobarbaz]</font>' },
{ 'id': 'FN:c_FONTf:a.s:ff:v-1_SW',
'rte1-id': 'c-fontname-2',
'desc': 'Change existing font name with conflicting face and style to new font name (without CSS)',
'value': 'courier',
'pad': '<font face="arial" style="font-family: verdana">[foobarbaz]</font>',
'expected': '<font face="courier">[foobarbaz]</font>' },
{ 'id': 'FN:c_FONTf:a-1_SI',
'desc': 'Change existing font name to new font name, text partially selected',
'value': 'courier',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': '<font face="arial">foo</font><font face="courier">[bar]</font><font face="arial">baz</font>',
'accept': '<font face="arial">foo<font face="courier">[bar]</font>baz</font>' },
{ 'id': 'FN:c_FONTf:a-2_SL',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': 'foo[bar<font face="arial">baz]qoz</font>',
'expected': 'foo<font face="courier">[barbaz]</font><font face="arial">qoz</font>' },
{ 'id': 'FN:c_FONTf:v-FONTf:a-1_SW',
'rte1-id': 'c-fontname-3',
'desc': 'Change existing font name in nested <font> tags to new font name (without CSS)',
'value': 'courier',
'pad': '<font face="verdana"><font face="arial">[foobarbaz]</font></font>',
'expected': '<font face="courier">[foobarbaz]</font>',
'accept': '<font face="verdana"><font face="courier">[foobarbaz]</font></font>' },
{ 'id': 'FN:c_SPANs:ff:v-FONTf:a-1_SW',
'rte1-id': 'c-fontname-4',
'desc': 'Change existing font name in nested mixed tags to new font name (without CSS)',
'value': 'courier',
'pad': '<span style="font-family: verdana"><font face="arial">[foobarbaz]</font></span>',
'expected': '<font face="courier">[foobarbaz]</font>',
'accept': '<span style="font-family: verdana"><font face="courier">[foobarbaz]</font></span>' }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:1_FONTsz:4-1_SW',
'rte1-id': 'c-fontsize-0',
'desc': 'Change existing font size to new size (without CSS)',
'value': '1',
'pad': '<font size="4">[foobarbaz]</font>',
'expected': '<font size="1">[foobarbaz]</font>' },
{ 'id': 'FS:1_SPAN.ass.s:fs:large-1_SW',
'rte1-id': 'c-fontsize-1',
'desc': 'Change existing font size from styled span to new size (without CSS)',
'value': '1',
'pad': '<span class="Apple-style-span" style="font-size: large">[foobarbaz]</span>',
'expected': '<font size="1">[foobarbaz]</font>' },
{ 'id': 'FS:5_FONTsz:1.s:fs:xs-1_SW',
'rte1-id': 'c-fontsize-2',
'desc': 'Change existing font size from tag with conflicting size and style to new size (without CSS)',
'value': '5',
'pad': '<font size="1" style="font-size:x-small">[foobarbaz]</font>',
'expected': '<font size="5">[foobarbaz]</font>' },
{ 'id': 'FS:2_FONTc:b.sz:6-1_SI',
'desc': 'Change the font size in content with a different font size and font color',
'value': '2',
'pad': '<font color="blue" size="6">foo[bar]baz</font>',
'expected': [ '<font color="blue" size="6">foo<font size="2">[bar]</font>baz</font>',
'<font color="blue"><font size="6">foo</font><font size="2">[bar]</font><font size="6">baz</font></font>' ] },
{ 'id': 'FS:larger_FONTsz:4',
'desc': 'Change selection to use next larger font',
'value': 'larger',
'pad': '<font size="4">foo[bar]baz</font>',
'expected': '<font size="4">foo<font size="larger">[bar]</font>baz</font>',
'accept': '<font size="4">foo</font><font size="5">[bar]</font><font size="4">baz</font>' },
{ 'id': 'FS:smaller_FONTsz:4',
'desc': 'Change selection to use next smaller font',
'value': 'smaller',
'pad': '<font size="4">foo[bar]baz</font>',
'expected': '<font size="4">foo<font size="smaller">[bar]</font>baz</font>',
'accept': '<font size="4">foo</font><font size="3">[bar]</font><font size="4">baz</font>' }
]
},
{ 'desc': '[MIDAS] formatblock',
'command': 'formatblock',
'tests': [
{ 'id': 'FB:h1_ADDRESS-1_SW',
'desc': 'change block from <address> to <h1>',
'value': 'h1',
'pad': '<address>foo [bar] baz</address>',
'expected': '<h1>foo [bar] baz</h1>' },
{ 'id': 'FB:h1_ADDRESS-FONTsz:4-1_SO',
'desc': 'change block from <address> with partially formatted content to <h1>',
'value': 'h1',
'pad': '<address>foo [<font size="4">bar</font>] baz</address>',
'expected': '<h1>foo [bar] baz</h1>' },
{ 'id': 'FB:h1_ADDRESS-FONTsz:4-1_SW',
'desc': 'change block from <address> with partially formatted content to <h1>',
'value': 'h1',
'pad': '<address>foo <font size="4">[bar]</font> baz</address>',
'expected': '<h1>foo [bar] baz</h1>' },
{ 'id': 'FB:h1_ADDRESS-FONT.ass.sz:4-1_SW',
'desc': 'change block from <address> with partially formatted content to <h1>',
'value': 'h1',
'pad': '<address>foo <font class="Apple-style-span" size="4">[bar]</font> baz</address>',
'expected': '<h1>foo [bar] baz</h1>' }
]
}
]
}

View File

@ -0,0 +1,210 @@
CHANGE_TESTS_CSS = {
'id': 'CC',
'caption': 'Change Existing Format to Different Format Tests, using styleWithCSS',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': True,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': '[HTML5] italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SL',
'desc': 'Italicize partially italicized text',
'pad': 'foo[bar<i>baz]</i>qoz',
'expected': 'foo<span style="font-style: italic">[barbaz]</span>qoz' },
{ 'id': 'I_B-1_SL',
'desc': 'Italicize partially bolded text',
'pad': 'foo[bar<b>baz]</b>qoz',
'expected': 'foo<span style="font-style: italic">[bar<b>baz]</b></span>qoz',
'accept': 'foo<span style="font-style: italic">[bar<b>baz</b>}</span>qoz' },
{ 'id': 'I_B-1_SW',
'desc': 'Italicize bold text, ideally combining both',
'pad': 'foobar<b>[baz]</b>qoz',
'expected': 'foobar<span style="font-style: italic; font-weight: bold">[baz]</span>qoz',
'accept': 'foobar<b><span style="font-style: italic">[baz]</span></b>qoz' }
]
},
{ 'desc': '[MIDAS] backcolor',
'command': 'backcolor',
'tests': [
{ 'id': 'BC:gray_SPANs:bc:b-1_SW',
'desc': 'Change background color from blue to gray',
'value': 'gray',
'pad': '<span style="background-color: blue">[foobarbaz]</span>',
'expected': '<span style="background-color: gray">[foobarbaz]</span>' },
{ 'id': 'BC:gray_SPANs:bc:b-1_SO',
'desc': 'Change background color from blue to gray',
'value': 'gray',
'pad': '{<span style="background-color: blue">foobarbaz</span>}',
'expected': [ '{<span style="background-color: gray">foobarbaz</span>}',
'<span style="background-color: gray">[foobarbaz]</span>' ] },
{ 'id': 'BC:gray_SPANs:bc:b-1_SI',
'desc': 'Change background color from blue to gray',
'value': 'gray',
'pad': '<span style="background-color: blue">foo[bar]baz</span>',
'expected': '<span style="background-color: blue">foo</span><span style="background-color: gray">[bar]</span><span style="background-color: blue">baz</span>',
'accept': '<span style="background-color: blue">foo<span style="background-color: gray">[bar]</span>baz</span>' },
{ 'id': 'BC:gray_P-SPANs:bc:b-1_SW',
'desc': 'Change background color within a paragraph from blue to gray',
'value': 'gray',
'pad': '<p><span style="background-color: blue">[foobarbaz]</span></p>',
'expected': [ '<p><span style="background-color: gray">[foobarbaz]</span></p>',
'<p style="background-color: gray">[foobarbaz]</p>' ] },
{ 'id': 'BC:gray_P-SPANs:bc:b-2_SW',
'desc': 'Change background color within a paragraph from blue to gray',
'value': 'gray',
'pad': '<p>foo<span style="background-color: blue">[bar]</span>baz</p>',
'expected': '<p>foo<span style="background-color: gray">[bar]</span>baz</p>' },
{ 'id': 'BC:gray_P-SPANs:bc:b-3_SO',
'desc': 'Change background color within a paragraph from blue to gray (selection encloses more than previous span)',
'value': 'gray',
'pad': '<p>[foo<span style="background-color: blue">barbaz</span>qoz]quz</p>',
'expected': '<p><span style="background-color: gray">[foobarbazqoz]</span>quz</p>' },
{ 'id': 'BC:gray_P-SPANs:bc:b-3_SL',
'desc': 'Change background color within a paragraph from blue to gray (previous span partially selected)',
'value': 'gray',
'pad': '<p>[foo<span style="background-color: blue">bar]baz</span>qozquz</p>',
'expected': '<p><span style="background-color: gray">[foobar]</span><span style="background-color: blue">baz</span>qozquz</p>' },
{ 'id': 'BC:gray_SPANs:bc:b-2_SL',
'desc': 'Change background color from blue to gray on partially covered span, selection extends left',
'value': 'gray',
'pad': 'foo [bar <span style="background-color: blue">baz] qoz</span> quz sic',
'expected': 'foo <span style="background-color: gray">[bar baz]</span><span style="background-color: blue"> qoz</span> quz sic' },
{ 'id': 'BC:gray_SPANs:bc:b-2_SR',
'desc': 'Change background color from blue to gray on partially covered span, selection extends right',
'value': 'gray',
'pad': 'foo bar <span style="background-color: blue">baz [qoz</span> quz] sic',
'expected': 'foo bar <span style="background-color: blue">baz </span><span style="background-color: gray">[qoz quz]</span> sic' }
]
},
{ 'desc': '[MIDAS] fontname',
'command': 'fontname',
'tests': [
{ 'id': 'FN:c_SPANs:ff:a-1_SW',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': '<span style="font-family: arial">[foobarbaz]</span>',
'expected': '<span style="font-family: courier">[foobarbaz]</span>' },
{ 'id': 'FN:c_FONTf:a-1_SW',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': '<font face="arial">[foobarbaz]</font>',
'expected': [ '<font style="font-family: courier">[foobarbaz]</font>',
'<span style="font-family: courier">[foobarbaz]</span>' ] },
{ 'id': 'FN:c_FONTf:a-1_SI',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': '<font face="arial">foo</font><span style="font-family: courier">[bar]</span><font face="arial">baz</font>' },
{ 'id': 'FN:a_FONTf:a-1_SI',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop)',
'value': 'arial',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': '<font face="arial">foo[bar]baz</font>' },
{ 'id': 'FN:a_FONTf:a-1_SW',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop or perhaps change tag)',
'value': 'arial',
'pad': '<font face="arial">[foobarbaz]</font>',
'expected': [ '<font face="arial">[foobarbaz]</font>',
'<span style="font-family: arial">[foobarbaz]</span>' ] },
{ 'id': 'FN:a_FONTf:a-1_SO',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop or perhaps change tag)',
'value': 'arial',
'pad': '{<font face="arial">foobarbaz</font>}',
'expected': [ '{<font face="arial">foobarbaz</font>}',
'<font face="arial">[foobarbaz]</font>',
'{<span style="font-family: arial">foobarbaz</span>}',
'<span style="font-family: arial">[foobarbaz]</span>' ] },
{ 'id': 'FN:a_SPANs:ff:a-1_SI',
'desc': 'Change existing font name to same font name, using CSS styling (should be noop)',
'value': 'arial',
'pad': '<span style="font-family: arial">[foobarbaz]</span>',
'expected': '<span style="font-family: arial">[foobarbaz]</span>' },
{ 'id': 'FN:c_FONTf:a-2_SL',
'desc': 'Change existing font name to new font name, using CSS styling',
'value': 'courier',
'pad': 'foo[bar<font face="arial">baz]qoz</font>',
'expected': 'foo<span style="font-family: courier">[barbaz]</span><font face="arial">qoz</font>' }
]
},
{ 'desc': '[MIDAS] fontsize',
'command': 'fontsize',
'tests': [
{ 'id': 'FS:1_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to new size, using CSS styling',
'value': '1',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': '<span style="font-size: x-small">[foobarbaz]</span>' },
{ 'id': 'FS:large_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to same size (should be noop)',
'value': 'large',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': '<span style="font-size: large">[foobarbaz]</span>' },
{ 'id': 'FS:18px_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to equivalent px size (should be noop, or change unit)',
'value': '18px',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': [ '<span style="font-size: 18px">[foobarbaz]</span>',
'<span style="font-size: large">[foobarbaz]</span>' ] },
{ 'id': 'FS:4_SPANs:fs:l-1_SW',
'desc': 'Change existing font size to equivalent numeric size (should be noop)',
'value': '4',
'pad': '<span style="font-size: large">[foobarbaz]</span>',
'expected': '<span style="font-size: large">[foobarbaz]</span>' },
{ 'id': 'FS:4_SPANs:fs:18px-1_SW',
'desc': 'Change existing font size to equivalent numeric size (should be noop)',
'value': '4',
'pad': '<span style="font-size: 18px">[foobarbaz]</span>',
'expected': '<span style="font-size: 18px">[foobarbaz]</span>' },
{ 'id': 'FS:larger_SPANs:fs:l-1_SI',
'desc': 'Change selection to use next larger font',
'value': 'larger',
'pad': '<span style="font-size: large">foo[bar]baz</span>',
'expected': [ '<span style="font-size: large">foo<span style="font-size: x-large">[bar]</span>baz</span>',
'<span style="font-size: large">foo</span><span style="font-size: x-large">[bar]</span><span style="font-size: large">baz</span>' ],
'accept': '<span style="font-size: large">foo<font size="larger">[bar]</font>baz</span>' },
{ 'id': 'FS:smaller_SPANs:fs:l-1_SI',
'desc': 'Change selection to use next smaller font',
'value': 'smaller',
'pad': '<span style="font-size: large">foo[bar]baz</span>',
'expected': [ '<span style="font-size: large">foo<span style="font-size: medium">[bar]</span>baz</span>',
'<span style="font-size: large">foo</span><span style="font-size: medium">[bar]</span><span style="font-size: large">baz</span>' ],
'accept': '<span style="font-size: large">foo<font size="smaller">[bar]</font>baz</span>' }
]
}
]
}

View File

@ -0,0 +1,330 @@
DELETE_TESTS = {
'id': 'D',
'caption': 'Delete Tests',
'command': 'delete',
'checkAttrs': True,
'checkStyle': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'delete single characters',
'tests': [
{ 'id': 'CHAR-1_SC',
'desc': 'Delete 1 character',
'pad': 'foo^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-2_SC',
'desc': 'Delete 1 pre-composed character o with diaeresis',
'pad': 'fo&#xF6;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-3_SC',
'desc': 'Delete 1 character with combining diaeresis above',
'pad': 'foo&#x0308;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-4_SC',
'desc': 'Delete 1 character with combining diaeresis below',
'pad': 'foo&#x0324;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SC',
'desc': 'Delete 1 character with combining diaeresis above and below',
'pad': 'foo&#x0308;&#x0324;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SI-1',
'desc': 'Delete 1 character with combining diaeresis above and below, selection on diaeresis above',
'pad': 'foo[&#x0308;]&#x0324;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SI-2',
'desc': 'Delete 1 character with combining diaeresis above and below, selection on diaeresis below',
'pad': 'foo&#x0308;[&#x0324;]barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SR',
'desc': 'Delete 1 character with combining diaeresis above and below, selection oblique on diaeresis and following text',
'pad': 'foo&#x0308;[&#x0324;bar]baz',
'expected': 'fo^baz' },
{ 'id': 'CHAR-6_SC',
'desc': 'Delete 1 character with enclosing square',
'pad': 'foo&#x20DE;^barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-7_SC',
'desc': 'Delete 1 character with combining long solidus overlay',
'pad': 'foo&#x0338;^barbaz',
'expected': 'fo^barbaz' }
]
},
{ 'desc': 'delete text selection',
'tests': [
{ 'id': 'TEXT-1_SI',
'desc': 'Delete text selection',
'pad': 'foo[bar]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SS',
'desc': 'Delete at start of span',
'pad': 'foo<b>^bar</b>baz',
'expected': 'fo^<b>bar</b>baz' },
{ 'id': 'B-1_SA',
'desc': 'Delete from position after span',
'pad': 'foo<b>bar</b>^baz',
'expected': 'foo<b>ba^</b>baz' },
{ 'id': 'B-1_SW',
'desc': 'Delete selection that wraps the whole span content',
'pad': 'foo<b>[bar]</b>baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SO',
'desc': 'Delete selection that wraps the whole span',
'pad': 'foo[<b>bar</b>]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SL',
'desc': 'Delete oblique selection that starts before span',
'pad': 'foo[bar<b>baz]quoz</b>quuz',
'expected': 'foo^<b>quoz</b>quuz' },
{ 'id': 'B-1_SR',
'desc': 'Delete oblique selection that ends after span',
'pad': 'foo<b>bar[baz</b>quoz]quuz',
'expected': 'foo<b>bar^</b>quuz' },
{ 'id': 'B.I-1_SM',
'desc': 'Delete oblique selection that starts and ends in different spans',
'pad': 'foo<b>bar[baz</b><i>qoz]quuz</i>quuuz',
'expected': 'foo<b>bar^</b><i>quuz</i>quuuz' },
{ 'id': 'GEN-1_SS',
'desc': 'Delete at start of span with generated content',
'pad': 'foo<gen>^bar</gen>baz',
'expected': 'fo^<gen>bar</gen>baz' },
{ 'id': 'GEN-1_SA',
'desc': 'Delete from position after span with generated content',
'pad': 'foo<gen>bar</gen>^baz',
'expected': 'foo<gen>ba^</gen>baz' }
]
},
{ 'desc': 'delete paragraphs',
'tests': [
{ 'id': 'P2-1_SS2',
'desc': 'Delete from collapsed selection at start of paragraph - should merge with previous',
'pad': '<p>foobar</p><p>^bazqoz</p>',
'expected': '<p>foobar^bazqoz</p>' },
{ 'id': 'P2-1_SI2',
'desc': 'Delete non-collapsed selection at start of paragraph - should not merge with previous',
'pad': '<p>foobar</p><p>[baz]qoz</p>',
'expected': '<p>foobar</p><p>^qoz</p>' },
{ 'id': 'P2-1_SM',
'desc': 'Delete non-collapsed selection spanning 2 paragraphs - should merge them',
'pad': '<p>foo[bar</p><p>baz]qoz</p>',
'expected': '<p>foo^qoz</p>' }
]
},
{ 'desc': 'delete lists and list items',
'tests': [
{ 'id': 'OL-LI2-1_SO1',
'desc': 'Delete fully wrapped list item',
'pad': 'foo<ol>{<li>bar</li>}<li>baz</li></ol>qoz',
'expected': ['foo<ol>|<li>baz</li></ol>qoz',
'foo<ol><li>^baz</li></ol>qoz'] },
{ 'id': 'OL-LI2-1_SM',
'desc': 'Delete oblique range between list items within same list',
'pad': 'foo<ol><li>ba[r</li><li>b]az</li></ol>qoz',
'expected': 'foo<ol><li>ba^az</li></ol>qoz' },
{ 'id': 'OL-LI-1_SW',
'desc': 'Delete contents of last list item (list should remain)',
'pad': 'foo<ol><li>[foo]</li></ol>qoz',
'expected': ['foo<ol><li>|</li></ol>qoz',
'foo<ol><li>^</li></ol>qoz'] },
{ 'id': 'OL-LI-1_SO',
'desc': 'Delete last list item of list (should remove entire list)',
'pad': 'foo<ol>{<li>foo</li>}</ol>qoz',
'expected': 'foo^qoz' }
]
},
{ 'desc': 'delete with strange selections',
'tests': [
{ 'id': 'HR.BR-1_SM',
'desc': 'Delete selection that starts and ends within nodes that don\'t have children',
'pad': 'foo<hr {>bar<br }>baz',
'expected': 'foo<hr>|<br>baz' }
]
},
{ 'desc': 'delete after table',
'tests': [
{ 'id': 'TABLE-1_SA',
'desc': 'Delete from position immediately after table (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar</td></tr></tbody></table>^baz',
'expected': 'foo<table><tbody><tr><td>bar</td></tr></tbody></table>^baz' }
]
},
{ 'desc': 'delete within table cells',
'tests': [
{ 'id': 'TD-1_SS',
'desc': 'Delete from start of first cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>^bar</td></tr></tbody></table>baz',
'expected': 'foo<table><tbody><tr><td>^bar</td></tr></tbody></table>baz' },
{ 'id': 'TD2-1_SS2',
'desc': 'Delete from start of inner cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar</td><td>^baz</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>bar</td><td>^baz</td></tr></tbody></table>quoz' },
{ 'id': 'TD2-1_SM',
'desc': 'Delete with selection spanning 2 cells',
'pad': 'foo<table><tbody><tr><td>ba[r</td><td>b]az</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>ba^</td><td>az</td></tr></tbody></table>quoz' }
]
},
{ 'desc': 'delete table rows',
'tests': [
{ 'id': 'TR3-1_SO1',
'desc': 'Delete first table row',
'pad': '<table><tbody>{<tr><td>A</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO2',
'desc': 'Delete middle table row',
'pad': '<table><tbody><tr><td>A</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO3',
'desc': 'Delete last table row',
'pad': '<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>B^</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 2',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=2>R</td></tr>}<tr><td>B</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td><td>R</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td><td>R</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO2',
'desc': 'Delete second table row where a cell has rowspan 2',
'pad': '<table><tbody><tr><td>A</td><td rowspan=2>R</td></tr>{<tr><td>B</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td>R</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td>R^</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 3',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=3>R</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO2',
'desc': 'Delete middle table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO3',
'desc': 'Delete last table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B^</td></tr></tbody></table>'] }
]
},
{ 'desc': 'delete with non-editable nested content',
'tests': [
{ 'id': 'DIV:ce:false-1_SO',
'desc': 'Delete nested non-editable <div>',
'pad': 'foo[bar<div contenteditable="false">NESTED</div>baz]qoz',
'expected': 'foo^qoz' },
{ 'id': 'DIV:ce:false-1_SB',
'desc': 'Delete from immediately after a nested non-editable <div> (should be no-op)',
'pad': 'foobar<div contenteditable="false">NESTED</div>^bazqoz',
'expected': 'foobar<div contenteditable="false">NESTED</div>^bazqoz' },
{ 'id': 'DIV:ce:false-1_SL',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foo[bar<div contenteditable="false">NES]TED</div>bazqoz',
'expected': [ 'foo^<div contenteditable="false">NESTED</div>bazqoz',
'foo<div contenteditable="false">[NES]TED</div>bazqoz' ] },
{ 'id': 'DIV:ce:false-1_SR',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foobar<div contenteditable="false">NES[TED</div>baz]qoz',
'expected': [ 'foobar<div contenteditable="false">NESTED</div>^qoz',
'foobar<div contenteditable="false">NES[TED]</div>qoz' ] },
{ 'id': 'DIV:ce:false-1_SI',
'desc': 'Delete inside nested non-editable <div> (should be no-op)',
'pad': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz',
'expected': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz' }
]
},
{ 'desc': 'Delete with display:inline-block',
'checkStyle': True,
'tests': [
{ 'id': 'SPAN:d:ib-1_SC',
'desc': 'Delete inside an inline-block <span>',
'pad': 'foo<span style="display: inline-block">bar^baz</span>qoz',
'expected': 'foo<span style="display: inline-block">ba^baz</span>qoz' },
{ 'id': 'SPAN:d:ib-1_SA',
'desc': 'Delete from immediately after an inline-block <span>',
'pad': 'foo<span style="display: inline-block">barbaz</span>^qoz',
'expected': 'foo<span style="display: inline-block">barba^</span>qoz' },
{ 'id': 'SPAN:d:ib-2_SL',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo[DEL<span style="display: inline-block">ETE]bar</span>baz',
'expected': 'foo^<span style="display: inline-block">bar</span>baz' },
{ 'id': 'SPAN:d:ib-3_SR',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DEL</span>ETE]baz',
'expected': 'foo<span style="display: inline-block">bar^</span>baz' },
{ 'id': 'SPAN:d:ib-4i_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DELETE]baz</span>qoz',
'expected': 'foo<span style="display: inline-block">bar^baz</span>qoz' },
{ 'id': 'SPAN:d:ib-4l_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">[DELETE]barbaz</span>qoz',
'expected': 'foo<span style="display: inline-block">^barbaz</span>qoz' },
{ 'id': 'SPAN:d:ib-4r_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">barbaz[DELETE]</span>qoz',
'expected': 'foo<span style="display: inline-block">barbaz^</span>qoz' }
]
}
]
}

View File

@ -0,0 +1,315 @@
FORWARDDELETE_TESTS = {
'id': 'FD',
'caption': 'Forward-Delete Tests',
'command': 'forwardDelete',
'checkAttrs': True,
'checkStyle': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'forward-delete single characters',
'tests': [
{ 'id': 'CHAR-1_SC',
'desc': 'Delete 1 character',
'pad': 'foo^barbaz',
'expected': 'foo^arbaz' },
{ 'id': 'CHAR-2_SC',
'desc': 'Delete 1 pre-composed character o with diaeresis',
'pad': 'fo^&#xF6;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-3_SC',
'desc': 'Delete 1 character with combining diaeresis above',
'pad': 'fo^o&#x0308;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-4_SC',
'desc': 'Delete 1 character with combining diaeresis below',
'pad': 'fo^o&#x0324;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-5_SC',
'desc': 'Delete 1 character with combining diaeresis above and below',
'pad': 'fo^o&#x0308;&#x0324;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-6_SC',
'desc': 'Delete 1 character with enclosing square',
'pad': 'fo^o&#x20DE;barbaz',
'expected': 'fo^barbaz' },
{ 'id': 'CHAR-7_SC',
'desc': 'Delete 1 character with combining long solidus overlay',
'pad': 'fo^o&#x0338;barbaz',
'expected': 'fo^barbaz' }
]
},
{ 'desc': 'forward-delete text selections',
'tests': [
{ 'id': 'TEXT-1_SI',
'desc': 'Delete text selection',
'pad': 'foo[bar]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SE',
'desc': 'Forward-delete at end of span',
'pad': 'foo<b>bar^</b>baz',
'expected': 'foo<b>bar^</b>az' },
{ 'id': 'B-1_SB',
'desc': 'Forward-delete from position before span',
'pad': 'foo^<b>bar</b>baz',
'expected': 'foo^<b>ar</b>baz' },
{ 'id': 'B-1_SW',
'desc': 'Delete selection that wraps the whole span content',
'pad': 'foo<b>[bar]</b>baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SO',
'desc': 'Delete selection that wraps the whole span',
'pad': 'foo[<b>bar</b>]baz',
'expected': 'foo^baz' },
{ 'id': 'B-1_SL',
'desc': 'Delete oblique selection that starts before span',
'pad': 'foo[bar<b>baz]quoz</b>quuz',
'expected': 'foo^<b>quoz</b>quuz' },
{ 'id': 'B-1_SR',
'desc': 'Delete oblique selection that ends after span',
'pad': 'foo<b>bar[baz</b>quoz]quuz',
'expected': 'foo<b>bar^</b>quuz' },
{ 'id': 'B.I-1_SM',
'desc': 'Delete oblique selection that starts and ends in different spans',
'pad': 'foo<b>bar[baz</b><i>qoz]quuz</i>quuuz',
'expected': 'foo<b>bar^</b><i>quuz</i>quuuz' },
{ 'id': 'GEN-1_SE',
'desc': 'Delete at end of span with generated content',
'pad': 'foo<gen>bar^</gen>baz',
'expected': 'foo<gen>bar^</gen>az' },
{ 'id': 'GEN-1_SB',
'desc': 'Delete from position before span with generated content',
'pad': 'foo^<gen>bar</gen>baz',
'expected': 'foo^<gen>ar</gen>baz' }
]
},
{ 'desc': 'forward-delete paragraphs',
'tests': [
{ 'id': 'P2-1_SE1',
'desc': 'Delete from collapsed selection at end of paragraph - should merge with next',
'pad': '<p>foobar^</p><p>bazqoz</p>',
'expected': '<p>foobar^bazqoz</p>' },
{ 'id': 'P2-1_SI1',
'desc': 'Delete non-collapsed selection at end of paragraph - should not merge with next',
'pad': '<p>foo[bar]</p><p>bazqoz</p>',
'expected': '<p>foo^</p><p>bazqoz</p>' },
{ 'id': 'P2-1_SM',
'desc': 'Delete non-collapsed selection spanning 2 paragraphs - should merge them',
'pad': '<p>foo[bar</p><p>baz]qoz</p>',
'expected': '<p>foo^qoz</p>' }
]
},
{ 'desc': 'forward-delete lists and list items',
'tests': [
{ 'id': 'OL-LI2-1_SO1',
'desc': 'Delete fully wrapped list item',
'pad': 'foo<ol>{<li>bar</li>}<li>baz</li></ol>qoz',
'expected': ['foo<ol>|<li>baz</li></ol>qoz',
'foo<ol><li>^baz</li></ol>qoz'] },
{ 'id': 'OL-LI2-1_SM',
'desc': 'Delete oblique range between list items within same list',
'pad': 'foo<ol><li>ba[r</li><li>b]az</li></ol>qoz',
'expected': 'foo<ol><li>ba^az</li></ol>qoz' },
{ 'id': 'OL-LI-1_SW',
'desc': 'Delete contents of last list item (list should remain)',
'pad': 'foo<ol><li>[foo]</li></ol>qoz',
'expected': ['foo<ol><li>|</li></ol>qoz',
'foo<ol><li>^</li></ol>qoz'] },
{ 'id': 'OL-LI-1_SO',
'desc': 'Delete last list item of list (should remove entire list)',
'pad': 'foo<ol>{<li>foo</li>}</ol>qoz',
'expected': 'foo^qoz' }
]
},
{ 'desc': 'forward-delete with strange selections',
'tests': [
{ 'id': 'HR.BR-1_SM',
'desc': 'Delete selection that starts and ends within nodes that don\'t have children',
'pad': 'foo<hr {>bar<br }>baz',
'expected': 'foo<hr>|<br>baz' }
]
},
{ 'desc': 'forward-delete from immediately before a table',
'tests': [
{ 'id': 'TABLE-1_SB',
'desc': 'Delete from position immediately before table (should have no effect)',
'pad': 'foo^<table><tbody><tr><td>bar</td></tr></tbody></table>baz',
'expected': 'foo^<table><tbody><tr><td>bar</td></tr></tbody></table>baz' }
]
},
{ 'desc': 'forward-delete within table cells',
'tests': [
{ 'id': 'TD-1_SE',
'desc': 'Delete from end of last cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar^</td></tr></tbody></table>baz',
'expected': 'foo<table><tbody><tr><td>bar^</td></tr></tbody></table>baz' },
{ 'id': 'TD2-1_SE1',
'desc': 'Delete from end of inner cell (should have no effect)',
'pad': 'foo<table><tbody><tr><td>bar^</td><td>baz</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>bar^</td><td>baz</td></tr></tbody></table>quoz' },
{ 'id': 'TD2-1_SM',
'desc': 'Delete with selection spanning 2 cells',
'pad': 'foo<table><tbody><tr><td>ba[r</td><td>b]az</td></tr></tbody></table>quoz',
'expected': 'foo<table><tbody><tr><td>ba^</td><td>az</td></tr></tbody></table>quoz' }
]
},
{ 'desc': 'forward-delete table rows',
'tests': [
{ 'id': 'TR3-1_SO1',
'desc': 'Delete first table row',
'pad': '<table><tbody>{<tr><td>A</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO2',
'desc': 'Delete middle table row',
'pad': '<table><tbody><tr><td>A</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3-1_SO3',
'desc': 'Delete last table row',
'pad': '<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td></tr><tr><td>B^</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 2',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=2>R</td></tr>}<tr><td>B</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>B</td><td>R</td></tr></tbody></table>',
'<table><tbody><tr><td>^B</td><td>R</td></tr></tbody></table>'] },
{ 'id': 'TR2rs:2-1_SO2',
'desc': 'Delete second table row where a cell has rowspan 2',
'pad': '<table><tbody><tr><td>A</td><td rowspan=2>R</td></tr>{<tr><td>B</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td>R</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td>R^</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO1',
'desc': 'Delete first table row where a cell has rowspan 3',
'pad': '<table><tbody>{<tr><td>A</td><td rowspan=3>R</td></tr>}<tr><td>B</td></tr><tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody>|<tr><td>A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>^A</td><td rowspan="2">R</td></tr><tr><td>C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO2',
'desc': 'Delete middle table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr>{<tr><td>B</td></tr>}<tr><td>C</td></tr></tbody></table>',
'expected': ['<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr>|<tr><td>C</td></tr></tbody></table>',
'<table><tbody><tr><td>B</td><td rowspan="2">R</td></tr><tr><td>^C</td></tr></tbody></table>'] },
{ 'id': 'TR3rs:3-1_SO3',
'desc': 'Delete last table row where a cell has rowspan 3',
'pad': '<table><tbody><tr><td>A</td><td rowspan=3>R</td></tr><tr><td>B</td></tr>{<tr><td>C</td></tr>}</tbody></table>',
'expected': ['<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B</td></tr>|</tbody></table>',
'<table><tbody><tr><td>A</td><td rowspan="2">R</td></tr><tr><td>B^</td></tr></tbody></table>'] }
]
},
{ 'desc': 'delete with non-editable nested content',
'tests': [
{ 'id': 'DIV:ce:false-1_SO',
'desc': 'Delete nested non-editable <div>',
'pad': 'foo[bar<div contenteditable="false">NESTED</div>baz]qoz',
'expected': 'foo^qoz' },
{ 'id': 'DIV:ce:false-1_SB',
'desc': 'Delete from immediately before a nested non-editable <div> (should be no-op)',
'pad': 'foobar^<div contenteditable="false">NESTED</div>bazqoz',
'expected': 'foobar^<div contenteditable="false">NESTED</div>bazqoz' },
{ 'id': 'DIV:ce:false-1_SL',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foo[bar<div contenteditable="false">NES]TED</div>bazqoz',
'expected': [ 'foo^<div contenteditable="false">NESTED</div>bazqoz',
'foo<div contenteditable="false">[NES]TED</div>bazqoz' ] },
{ 'id': 'DIV:ce:false-1_SR',
'desc': 'Delete nested non-editable <div> with oblique selection',
'pad': 'foobar<div contenteditable="false">NES[TED</div>baz]qoz',
'expected': [ 'foobar<div contenteditable="false">NESTED</div>^qoz',
'foobar<div contenteditable="false">NES[TED]</div>qoz' ] },
{ 'id': 'DIV:ce:false-1_SI',
'desc': 'Delete inside nested non-editable <div> (should be no-op)',
'pad': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz',
'expected': 'foobar<div contenteditable="false">NE[ST]ED</div>bazqoz' }
]
},
{ 'desc': 'Delete with display:inline-block',
'checkStyle': True,
'tests': [
{ 'id': 'SPAN:d:ib-1_SC',
'desc': 'Delete inside an inline-block <span>',
'pad': 'foo<span style="display: inline-block">bar^baz</span>qoz',
'expected': 'foo<span style="display: inline-block">bar^az</span>qoz' },
{ 'id': 'SPAN:d:ib-1_SA',
'desc': 'Delete from immediately before an inline-block <span>',
'pad': 'foo^<span style="display: inline-block">barbaz</span>qoz',
'expected': 'foo^<span style="display: inline-block">arbaz</span>qoz' },
{ 'id': 'SPAN:d:ib-2_SL',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo[DEL<span style="display: inline-block">ETE]bar</span>baz',
'expected': 'foo^<span style="display: inline-block">bar</span>baz' },
{ 'id': 'SPAN:d:ib-3_SR',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DEL</span>ETE]baz',
'expected': 'foo<span style="display: inline-block">bar^</span>baz' },
{ 'id': 'SPAN:d:ib-4i_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">bar[DELETE]baz</span>qoz',
'expected': 'foo<span style="display: inline-block">bar^baz</span>qoz' },
{ 'id': 'SPAN:d:ib-4l_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">[DELETE]barbaz</span>qoz',
'expected': 'foo<span style="display: inline-block">^barbaz</span>qoz' },
{ 'id': 'SPAN:d:ib-4r_SI',
'desc': 'Delete with nested inline-block <span>, oblique selection',
'pad': 'foo<span style="display: inline-block">barbaz[DELETE]</span>qoz',
'expected': 'foo<span style="display: inline-block">barbaz^</span>qoz' }
]
}
]
}

View File

@ -0,0 +1,285 @@
INSERT_TESTS = {
'id': 'I',
'caption': 'Insert Tests',
'checkAttrs': False,
'checkStyle': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'insert <hr>',
'command': 'inserthorizontalrule',
'tests': [
{ 'id': 'IHR_TEXT-1_SC',
'rte1-id': 'a-inserthorizontalrule-0',
'desc': 'Insert <hr> into text',
'pad': 'foo^bar',
'expected': 'foo<hr>^bar',
'accept': 'foo<hr>|bar' },
{ 'id': 'IHR_TEXT-1_SI',
'desc': 'Insert <hr>, replacing selected text',
'pad': 'foo[bar]baz',
'expected': 'foo<hr>^baz',
'accept': 'foo<hr>|baz' },
{ 'id': 'IHR_DIV-B-1_SX',
'desc': 'Insert <hr> between elements',
'pad': '<div><b>foo</b>|<b>bar</b></div>',
'expected': '<div><b>foo</b><hr>|<b>bar</b></div>' },
{ 'id': 'IHR_DIV-B-2_SO',
'desc': 'Insert <hr>, replacing a fully wrapped element',
'pad': '<div><b>foo</b>{<b>bar</b>}<b>baz</b></div>',
'expected': '<div><b>foo</b><hr>|<b>baz</b></div>' },
{ 'id': 'IHR_B-1_SC',
'desc': 'Insert <hr> into a span, splitting it',
'pad': '<b>foo^bar</b>',
'expected': '<b>foo</b><hr><b>^bar</b>' },
{ 'id': 'IHR_B-1_SS',
'desc': 'Insert <hr> into a span at the start (should not create an empty span)',
'pad': '<b>^foobar</b>',
'expected': '<hr><b>^foobar</b>' },
{ 'id': 'IHR_B-1_SE',
'desc': 'Insert <hr> into a span at the end',
'pad': '<b>foobar^</b>',
'expected': [ '<b>foobar</b><hr>|',
'<b>foobar</b><hr><b>^</b>' ] },
{ 'id': 'IHR_B-2_SL',
'desc': 'Insert <hr> with oblique selection starting outside of span',
'pad': 'foo[bar<b>baz]qoz</b>',
'expected': 'foo<hr>|<b>qoz</b>' },
{ 'id': 'IHR_B-2_SLR',
'desc': 'Insert <hr> with oblique reversed selection starting outside of span',
'pad': 'foo]bar<b>baz[qoz</b>',
'expected': [ 'foo<hr>|<b>qoz</b>',
'foo<hr><b>^qoz</b>' ] },
{ 'id': 'IHR_B-3_SR',
'desc': 'Insert <hr> with oblique selection ending outside of span',
'pad': '<b>foo[bar</b>baz]quoz',
'expected': [ '<b>foo</b><hr>|quoz',
'<b>foo</b><hr><b>^</b>quoz' ] },
{ 'id': 'IHR_B-3_SRR',
'desc': 'Insert <hr> with oblique reversed selection starting outside of span',
'pad': '<b>foo]bar</b>baz[quoz',
'expected': '<b>foo</b><hr>|quoz' },
{ 'id': 'IHR_B-I-1_SM',
'desc': 'Insert <hr> with oblique selection between different spans',
'pad': '<b>foo[bar</b><i>baz]quoz</i>',
'expected': [ '<b>foo</b><hr>|<i>quoz</i>',
'<b>foo</b><hr><b>^</b><i>quoz</i>' ] },
{ 'id': 'IHR_B-I-1_SMR',
'desc': 'Insert <hr> with reversed oblique selection between different spans',
'pad': '<b>foo]bar</b><i>baz[quoz</i>',
'expected': '<b>foo</b><hr><i>^quoz</i>' },
{ 'id': 'IHR_P-1_SC',
'desc': 'Insert <hr> into a paragraph, splitting it',
'pad': '<p>foo^bar</p>',
'expected': [ '<p>foo</p><hr>|<p>bar</p>',
'<p>foo</p><hr><p>^bar</p>' ] },
{ 'id': 'IHR_P-1_SS',
'desc': 'Insert <hr> into a paragraph at the start (should not create an empty span)',
'pad': '<p>^foobar</p>',
'expected': [ '<hr>|<p>foobar</p>',
'<hr><p>^foobar</p>' ] },
{ 'id': 'IHR_P-1_SE',
'desc': 'Insert <hr> into a paragraph at the end (should not create an empty span)',
'pad': '<p>foobar^</p>',
'expected': '<p>foobar</p><hr>|' }
]
},
{ 'desc': 'insert <p>',
'command': 'insertparagraph',
'tests': [
{ 'id': 'IP_P-1_SC',
'desc': 'Split paragraph',
'pad': '<p>foo^bar</p>',
'expected': '<p>foo</p><p>^bar</p>' },
{ 'id': 'IP_UL-LI-1_SC',
'desc': 'Split list item',
'pad': '<ul><li>foo^bar</li></ul>',
'expected': '<ul><li>foo</li><li>^bar</li></ul>' }
]
},
{ 'desc': 'insert text',
'command': 'inserttext',
'tests': [
{ 'id': 'ITEXT:text_TEXT-1_SC',
'desc': 'Insert text',
'value': 'text',
'pad': 'foo^bar',
'expected': 'footext^bar' },
{ 'id': 'ITEXT:text_TEXT-1_SI',
'desc': 'Insert text, replacing selected text',
'value': 'text',
'pad': 'foo[bar]baz',
'expected': 'footext^baz' }
]
},
{ 'desc': 'insert <br>',
'command': 'insertlinebreak',
'tests': [
{ 'id': 'IBR_TEXT-1_SC',
'desc': 'Insert <br> into text',
'pad': 'foo^bar',
'expected': [ 'foo<br>|bar',
'foo<br>^bar' ] },
{ 'id': 'IBR_TEXT-1_SI',
'desc': 'Insert <br>, replacing selected text',
'pad': 'foo[bar]baz',
'expected': [ 'foo<br>|baz',
'foo<br>^baz' ] },
{ 'id': 'IBR_LI-1_SC',
'desc': 'Insert <br> within list item',
'pad': '<ul><li>foo^bar</li></ul>',
'expected': '<ul><li>foo<br>^bar</li></ul>' }
]
},
{ 'desc': 'insert <img>',
'command': 'insertimage',
'tests': [
{ 'id': 'IIMG:url_TEXT-1_SC',
'rte1-id': 'a-insertimage-0',
'desc': 'Insert image with URL "http://goo.gl/bar.png"',
'value': 'http://goo.gl/bar.png',
'checkAttrs': True,
'pad': 'foo^bar',
'expected': [ 'foo<img src="http://goo.gl/bar.png">|bar',
'foo<img src="http://goo.gl/bar.png">^bar' ] },
{ 'id': 'IIMG:url_IMG-1_SO',
'desc': 'Change existing image to new URL, selection on <img>',
'value': 'http://baz.com/quz.png',
'checkAttrs': True,
'pad': '<span>foo{<img src="http://goo.gl/bar.png">}bar</span>',
'expected': [ '<span>foo<img src="http://baz.com/quz.png"/>|bar</span>',
'<span>foo<img src="http://baz.com/quz.png"/>^bar</span>' ] },
{ 'id': 'IIMG:url_SPAN-IMG-1_SO',
'desc': 'Change existing image to new URL, selection in text surrounding <img>',
'value': 'http://baz.com/quz.png',
'checkAttrs': True,
'pad': 'foo[<img src="http://goo.gl/bar.png">]bar',
'expected': [ 'foo<img src="http://baz.com/quz.png"/>|bar',
'foo<img src="http://baz.com/quz.png"/>^bar' ] },
{ 'id': 'IIMG:._SPAN-IMG-1_SO',
'desc': 'Remove existing image or URL, selection on <img>',
'value': '',
'checkAttrs': True,
'pad': '<span>foo{<img src="http://goo.gl/bar.png">}bar</span>',
'expected': [ '<span>foo^bar</span>',
'<span>foo<img>|bar</span>',
'<span>foo<img>^bar</span>',
'<span>foo<img src="">|bar</span>',
'<span>foo<img src="">^bar</span>' ] },
{ 'id': 'IIMG:._IMG-1_SO',
'desc': 'Remove existing image or URL, selection in text surrounding <img>',
'value': '',
'checkAttrs': True,
'pad': 'foo[<img src="http://goo.gl/bar.png">]bar',
'expected': [ 'foo^bar',
'foo<img>|bar',
'foo<img>^bar',
'foo<img src="">|bar',
'foo<img src="">^bar' ] }
]
},
{ 'desc': 'insert <ol>',
'command': 'insertorderedlist',
'tests': [
{ 'id': 'IOL_TEXT-1_SC',
'rte1-id': 'a-insertorderedlist-0',
'desc': 'Insert ordered list on collapsed selection',
'pad': 'foo^bar',
'expected': '<ol><li>foo^bar</li></ol>' },
{ 'id': 'IOL_TEXT-1_SI',
'desc': 'Insert ordered list on selected text',
'pad': 'foo[bar]baz',
'expected': '<ol><li>foo[bar]baz</li></ol>' }
]
},
{ 'desc': 'insert <ul>',
'command': 'insertunorderedlist',
'tests': [
{ 'id': 'IUL_TEXT-1_SC',
'desc': 'Insert unordered list on collapsed selection',
'pad': 'foo^bar',
'expected': '<ul><li>foo^bar</li></ul>' },
{ 'id': 'IUL_TEXT-1_SI',
'rte1-id': 'a-insertunorderedlist-0',
'desc': 'Insert unordered list on selected text',
'pad': 'foo[bar]baz',
'expected': '<ul><li>foo[bar]baz</li></ul>' }
]
},
{ 'desc': 'insert arbitrary HTML',
'command': 'inserthtml',
'tests': [
{ 'id': 'IHTML:BR_TEXT-1_SC',
'rte1-id': 'a-inserthtml-0',
'desc': 'InsertHTML: <br>',
'value': '<br>',
'pad': 'foo^barbaz',
'expected': 'foo<br>^barbaz' },
{ 'id': 'IHTML:text_TEXT-1_SI',
'desc': 'InsertHTML: "NEW"',
'value': 'NEW',
'pad': 'foo[bar]baz',
'expected': 'fooNEW^baz' },
{ 'id': 'IHTML:S_TEXT-1_SI',
'desc': 'InsertHTML: "<span>NEW<span>"',
'value': '<span>NEW</span>',
'pad': 'foo[bar]baz',
'expected': 'foo<span>NEW</span>^baz' },
{ 'id': 'IHTML:H1.H2_TEXT-1_SI',
'desc': 'InsertHTML: "<h1>NEW</h1><h2>HTML</h2>"',
'value': '<h1>NEW</h1><h2>HTML</h2>',
'pad': 'foo[bar]baz',
'expected': 'foo<h1>NEW</h1><h2>HTML</h2>^baz' },
{ 'id': 'IHTML:P-B_TEXT-1_SI',
'desc': 'InsertHTML: "<p>NEW<b>HTML</b>!</p>"',
'value': '<p>NEW<b>HTML</b>!</p>',
'pad': 'foo[bar]baz',
'expected': 'foo<p>NEW<b>HTML</b>!</p>^baz' }
]
}
]
}

View File

@ -0,0 +1,215 @@
QUERYENABLED_TESTS = {
'id': 'QE',
'caption': 'queryCommandEnabled Tests',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'expected': True,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'HTML5 commands',
'tests': [
{ 'id': 'SELECTALL_TEXT-1',
'desc': 'check whether the "selectall" command is enabled',
'qcenabled': 'selectall' },
{ 'id': 'UNSELECT_TEXT-1',
'desc': 'check whether the "unselect" command is enabled',
'qcenabled': 'unselect' },
{ 'id': 'UNDO_TEXT-1',
'desc': 'check whether the "undo" command is enabled',
'qcenabled': 'undo' },
{ 'id': 'REDO_TEXT-1',
'desc': 'check whether the "redo" command is enabled',
'qcenabled': 'redo' },
{ 'id': 'BOLD_TEXT-1',
'desc': 'check whether the "bold" command is enabled',
'qcenabled': 'bold' },
{ 'id': 'ITALIC_TEXT-1',
'desc': 'check whether the "italic" command is enabled',
'qcenabled': 'italic' },
{ 'id': 'UNDERLINE_TEXT-1',
'desc': 'check whether the "underline" command is enabled',
'qcenabled': 'underline' },
{ 'id': 'STRIKETHROUGH_TEXT-1',
'desc': 'check whether the "strikethrough" command is enabled',
'qcenabled': 'strikethrough' },
{ 'id': 'SUBSCRIPT_TEXT-1',
'desc': 'check whether the "subscript" command is enabled',
'qcenabled': 'subscript' },
{ 'id': 'SUPERSCRIPT_TEXT-1',
'desc': 'check whether the "superscript" command is enabled',
'qcenabled': 'superscript' },
{ 'id': 'FORMATBLOCK_TEXT-1',
'desc': 'check whether the "formatblock" command is enabled',
'qcenabled': 'formatblock' },
{ 'id': 'CREATELINK_TEXT-1',
'desc': 'check whether the "createlink" command is enabled',
'qcenabled': 'createlink' },
{ 'id': 'UNLINK_TEXT-1',
'desc': 'check whether the "unlink" command is enabled',
'qcenabled': 'unlink' },
{ 'id': 'INSERTHTML_TEXT-1',
'desc': 'check whether the "inserthtml" command is enabled',
'qcenabled': 'inserthtml' },
{ 'id': 'INSERTHORIZONTALRULE_TEXT-1',
'desc': 'check whether the "inserthorizontalrule" command is enabled',
'qcenabled': 'inserthorizontalrule' },
{ 'id': 'INSERTIMAGE_TEXT-1',
'desc': 'check whether the "insertimage" command is enabled',
'qcenabled': 'insertimage' },
{ 'id': 'INSERTLINEBREAK_TEXT-1',
'desc': 'check whether the "insertlinebreak" command is enabled',
'qcenabled': 'insertlinebreak' },
{ 'id': 'INSERTPARAGRAPH_TEXT-1',
'desc': 'check whether the "insertparagraph" command is enabled',
'qcenabled': 'insertparagraph' },
{ 'id': 'INSERTORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertorderedlist" command is enabled',
'qcenabled': 'insertorderedlist' },
{ 'id': 'INSERTUNORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertunorderedlist" command is enabled',
'qcenabled': 'insertunorderedlist' },
{ 'id': 'INSERTTEXT_TEXT-1',
'desc': 'check whether the "inserttext" command is enabled',
'qcenabled': 'inserttext' },
{ 'id': 'DELETE_TEXT-1',
'desc': 'check whether the "delete" command is enabled',
'qcenabled': 'delete' },
{ 'id': 'FORWARDDELETE_TEXT-1',
'desc': 'check whether the "forwarddelete" command is enabled',
'qcenabled': 'forwarddelete' }
]
},
{ 'desc': 'MIDAS commands',
'tests': [
{ 'id': 'STYLEWITHCSS_TEXT-1',
'desc': 'check whether the "styleWithCSS" command is enabled',
'qcenabled': 'styleWithCSS' },
{ 'id': 'CONTENTREADONLY_TEXT-1',
'desc': 'check whether the "contentreadonly" command is enabled',
'qcenabled': 'contentreadonly' },
{ 'id': 'BACKCOLOR_TEXT-1',
'desc': 'check whether the "backcolor" command is enabled',
'qcenabled': 'backcolor' },
{ 'id': 'FORECOLOR_TEXT-1',
'desc': 'check whether the "forecolor" command is enabled',
'qcenabled': 'forecolor' },
{ 'id': 'HILITECOLOR_TEXT-1',
'desc': 'check whether the "hilitecolor" command is enabled',
'qcenabled': 'hilitecolor' },
{ 'id': 'FONTNAME_TEXT-1',
'desc': 'check whether the "fontname" command is enabled',
'qcenabled': 'fontname' },
{ 'id': 'FONTSIZE_TEXT-1',
'desc': 'check whether the "fontsize" command is enabled',
'qcenabled': 'fontsize' },
{ 'id': 'INCREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "increasefontsize" command is enabled',
'qcenabled': 'increasefontsize' },
{ 'id': 'DECREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "decreasefontsize" command is enabled',
'qcenabled': 'decreasefontsize' },
{ 'id': 'HEADING_TEXT-1',
'desc': 'check whether the "heading" command is enabled',
'qcenabled': 'heading' },
{ 'id': 'INDENT_TEXT-1',
'desc': 'check whether the "indent" command is enabled',
'qcenabled': 'indent' },
{ 'id': 'OUTDENT_TEXT-1',
'desc': 'check whether the "outdent" command is enabled',
'qcenabled': 'outdent' },
{ 'id': 'CREATEBOOKMARK_TEXT-1',
'desc': 'check whether the "createbookmark" command is enabled',
'qcenabled': 'createbookmark' },
{ 'id': 'UNBOOKMARK_TEXT-1',
'desc': 'check whether the "unbookmark" command is enabled',
'qcenabled': 'unbookmark' },
{ 'id': 'JUSTIFYCENTER_TEXT-1',
'desc': 'check whether the "justifycenter" command is enabled',
'qcenabled': 'justifycenter' },
{ 'id': 'JUSTIFYFULL_TEXT-1',
'desc': 'check whether the "justifyfull" command is enabled',
'qcenabled': 'justifyfull' },
{ 'id': 'JUSTIFYLEFT_TEXT-1',
'desc': 'check whether the "justifyleft" command is enabled',
'qcenabled': 'justifyleft' },
{ 'id': 'JUSTIFYRIGHT_TEXT-1',
'desc': 'check whether the "justifyright" command is enabled',
'qcenabled': 'justifyright' },
{ 'id': 'REMOVEFORMAT_TEXT-1',
'desc': 'check whether the "removeformat" command is enabled',
'qcenabled': 'removeformat' },
{ 'id': 'COPY_TEXT-1',
'desc': 'check whether the "copy" command is enabled',
'qcenabled': 'copy' },
{ 'id': 'CUT_TEXT-1',
'desc': 'check whether the "cut" command is enabled',
'qcenabled': 'cut' },
{ 'id': 'PASTE_TEXT-1',
'desc': 'check whether the "paste" command is enabled',
'qcenabled': 'paste' }
]
},
{ 'desc': 'Other tests',
'tests': [
{ 'id': 'garbage-1_TEXT-1',
'desc': 'check correct return value with garbage input',
'qcenabled': '#!#@7',
'expected': False }
]
}
]
}

View File

@ -0,0 +1,214 @@
QUERYINDETERM_TESTS = {
'id': 'QI',
'caption': 'queryCommandIndeterm Tests',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'expected': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'HTML5 commands',
'tests': [
{ 'id': 'SELECTALL_TEXT-1',
'desc': 'check whether the "selectall" command is indeterminate',
'qcindeterm': 'selectall' },
{ 'id': 'UNSELECT_TEXT-1',
'desc': 'check whether the "unselect" command is indeterminate',
'qcindeterm': 'unselect' },
{ 'id': 'UNDO_TEXT-1',
'desc': 'check whether the "undo" command is indeterminate',
'qcindeterm': 'undo' },
{ 'id': 'REDO_TEXT-1',
'desc': 'check whether the "redo" command is indeterminate',
'qcindeterm': 'redo' },
{ 'id': 'BOLD_TEXT-1',
'desc': 'check whether the "bold" command is indeterminate',
'qcindeterm': 'bold' },
{ 'id': 'ITALIC_TEXT-1',
'desc': 'check whether the "italic" command is indeterminate',
'qcindeterm': 'italic' },
{ 'id': 'UNDERLINE_TEXT-1',
'desc': 'check whether the "underline" command is indeterminate',
'qcindeterm': 'underline' },
{ 'id': 'STRIKETHROUGH_TEXT-1',
'desc': 'check whether the "strikethrough" command is indeterminate',
'qcindeterm': 'strikethrough' },
{ 'id': 'SUBSCRIPT_TEXT-1',
'desc': 'check whether the "subscript" command is indeterminate',
'qcindeterm': 'subscript' },
{ 'id': 'SUPERSCRIPT_TEXT-1',
'desc': 'check whether the "superscript" command is indeterminate',
'qcindeterm': 'superscript' },
{ 'id': 'FORMATBLOCK_TEXT-1',
'desc': 'check whether the "formatblock" command is indeterminate',
'qcindeterm': 'formatblock' },
{ 'id': 'CREATELINK_TEXT-1',
'desc': 'check whether the "createlink" command is indeterminate',
'qcindeterm': 'createlink' },
{ 'id': 'UNLINK_TEXT-1',
'desc': 'check whether the "unlink" command is indeterminate',
'qcindeterm': 'unlink' },
{ 'id': 'INSERTHTML_TEXT-1',
'desc': 'check whether the "inserthtml" command is indeterminate',
'qcindeterm': 'inserthtml' },
{ 'id': 'INSERTHORIZONTALRULE_TEXT-1',
'desc': 'check whether the "inserthorizontalrule" command is indeterminate',
'qcindeterm': 'inserthorizontalrule' },
{ 'id': 'INSERTIMAGE_TEXT-1',
'desc': 'check whether the "insertimage" command is indeterminate',
'qcindeterm': 'insertimage' },
{ 'id': 'INSERTLINEBREAK_TEXT-1',
'desc': 'check whether the "insertlinebreak" command is indeterminate',
'qcindeterm': 'insertlinebreak' },
{ 'id': 'INSERTPARAGRAPH_TEXT-1',
'desc': 'check whether the "insertparagraph" command is indeterminate',
'qcindeterm': 'insertparagraph' },
{ 'id': 'INSERTORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertorderedlist" command is indeterminate',
'qcindeterm': 'insertorderedlist' },
{ 'id': 'INSERTUNORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertunorderedlist" command is indeterminate',
'qcindeterm': 'insertunorderedlist' },
{ 'id': 'INSERTTEXT_TEXT-1',
'desc': 'check whether the "inserttext" command is indeterminate',
'qcindeterm': 'inserttext' },
{ 'id': 'DELETE_TEXT-1',
'desc': 'check whether the "delete" command is indeterminate',
'qcindeterm': 'delete' },
{ 'id': 'FORWARDDELETE_TEXT-1',
'desc': 'check whether the "forwarddelete" command is indeterminate',
'qcindeterm': 'forwarddelete' }
]
},
{ 'desc': 'MIDAS commands',
'tests': [
{ 'id': 'STYLEWITHCSS_TEXT-1',
'desc': 'check whether the "styleWithCSS" command is indeterminate',
'qcindeterm': 'styleWithCSS' },
{ 'id': 'CONTENTREADONLY_TEXT-1',
'desc': 'check whether the "contentreadonly" command is indeterminate',
'qcindeterm': 'contentreadonly' },
{ 'id': 'BACKCOLOR_TEXT-1',
'desc': 'check whether the "backcolor" command is indeterminate',
'qcindeterm': 'backcolor' },
{ 'id': 'FORECOLOR_TEXT-1',
'desc': 'check whether the "forecolor" command is indeterminate',
'qcindeterm': 'forecolor' },
{ 'id': 'HILITECOLOR_TEXT-1',
'desc': 'check whether the "hilitecolor" command is indeterminate',
'qcindeterm': 'hilitecolor' },
{ 'id': 'FONTNAME_TEXT-1',
'desc': 'check whether the "fontname" command is indeterminate',
'qcindeterm': 'fontname' },
{ 'id': 'FONTSIZE_TEXT-1',
'desc': 'check whether the "fontsize" command is indeterminate',
'qcindeterm': 'fontsize' },
{ 'id': 'INCREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "increasefontsize" command is indeterminate',
'qcindeterm': 'increasefontsize' },
{ 'id': 'DECREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "decreasefontsize" command is indeterminate',
'qcindeterm': 'decreasefontsize' },
{ 'id': 'HEADING_TEXT-1',
'desc': 'check whether the "heading" command is indeterminate',
'qcindeterm': 'heading' },
{ 'id': 'INDENT_TEXT-1',
'desc': 'check whether the "indent" command is indeterminate',
'qcindeterm': 'indent' },
{ 'id': 'OUTDENT_TEXT-1',
'desc': 'check whether the "outdent" command is indeterminate',
'qcindeterm': 'outdent' },
{ 'id': 'CREATEBOOKMARK_TEXT-1',
'desc': 'check whether the "createbookmark" command is indeterminate',
'qcindeterm': 'createbookmark' },
{ 'id': 'UNBOOKMARK_TEXT-1',
'desc': 'check whether the "unbookmark" command is indeterminate',
'qcindeterm': 'unbookmark' },
{ 'id': 'JUSTIFYCENTER_TEXT-1',
'desc': 'check whether the "justifycenter" command is indeterminate',
'qcindeterm': 'justifycenter' },
{ 'id': 'JUSTIFYFULL_TEXT-1',
'desc': 'check whether the "justifyfull" command is indeterminate',
'qcindeterm': 'justifyfull' },
{ 'id': 'JUSTIFYLEFT_TEXT-1',
'desc': 'check whether the "justifyleft" command is indeterminate',
'qcindeterm': 'justifyleft' },
{ 'id': 'JUSTIFYRIGHT_TEXT-1',
'desc': 'check whether the "justifyright" command is indeterminate',
'qcindeterm': 'justifyright' },
{ 'id': 'REMOVEFORMAT_TEXT-1',
'desc': 'check whether the "removeformat" command is indeterminate',
'qcindeterm': 'removeformat' },
{ 'id': 'COPY_TEXT-1',
'desc': 'check whether the "copy" command is indeterminate',
'qcindeterm': 'copy' },
{ 'id': 'CUT_TEXT-1',
'desc': 'check whether the "cut" command is indeterminate',
'qcindeterm': 'cut' },
{ 'id': 'PASTE_TEXT-1',
'desc': 'check whether the "paste" command is indeterminate',
'qcindeterm': 'paste' }
]
},
{ 'desc': 'Other tests',
'tests': [
{ 'id': 'garbage-1_TEXT-1',
'desc': 'check correct return value with garbage input',
'qcindeterm': '#!#@7' }
]
}
]
}

View File

@ -0,0 +1,575 @@
QUERYSTATE_TESTS = {
'id': 'QS',
'caption': 'queryCommandState Tests',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'qcstate': '',
'tests': [
]
},
{ 'desc': 'query bold state',
'qcstate': 'bold',
'tests': [
{ 'id': 'B_TEXT_SI',
'rte1-id': 'q-bold-0',
'desc': 'query the "bold" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'B_B-1_SI',
'rte1-id': 'q-bold-1',
'desc': 'query the "bold" state',
'pad': '<b>foo[bar]baz</b>',
'expected': True },
{ 'id': 'B_STRONG-1_SI',
'rte1-id': 'q-bold-2',
'desc': 'query the "bold" state',
'pad': '<strong>foo[bar]baz</strong>',
'expected': True },
{ 'id': 'B_SPANs:fw:b-1_SI',
'rte1-id': 'q-bold-3',
'desc': 'query the "bold" state',
'pad': '<span style="font-weight: bold">foo[bar]baz</span>',
'expected': True },
{ 'id': 'B_SPANs:fw:n-1_SI',
'desc': 'query the "bold" state',
'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
'expected': False },
{ 'id': 'B_Bs:fw:n-1_SI',
'rte1-id': 'q-bold-4',
'desc': 'query the "bold" state',
'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
'expected': False },
{ 'id': 'B_B-SPANs:fw:n-1_SI',
'rte1-id': 'q-bold-5',
'desc': 'query the "bold" state',
'pad': '<b><span style="font-weight: normal">foo[bar]baz</span></b>',
'expected': False },
{ 'id': 'B_SPAN.b-1-SI',
'desc': 'query the "bold" state',
'pad': '<span class="b">foo[bar]baz</span>',
'expected': True },
{ 'id': 'B_MYB-1-SI',
'desc': 'query the "bold" state',
'pad': '<myb>foo[bar]baz</myb>',
'expected': True },
{ 'id': 'B_B-I-1_SC',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<b>foo<i>ba^r</i>baz</b>',
'expected': True },
{ 'id': 'B_B-I-1_SL',
'desc': 'query the "bold" state, selection partially in child element',
'pad': '<b>fo[o<i>b]ar</i>baz</b>',
'expected': True },
{ 'id': 'B_B-I-1_SR',
'desc': 'query the "bold" state, selection partially in child element',
'pad': '<b>foo<i>ba[r</i>b]az</b>',
'expected': True },
{ 'id': 'B_STRONG-I-1_SC',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<strong>foo<i>ba^r</i>baz</strong>',
'expected': True },
{ 'id': 'B_B-I-U-1_SC',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<b>foo<i>bar<u>b^az</u></i></strong>',
'expected': True },
{ 'id': 'B_B-I-U-1_SM',
'desc': 'query the "bold" state, bold tag not immediate parent',
'pad': '<b>foo<i>ba[r<u>b]az</u></i></strong>',
'expected': True },
{ 'id': 'B_TEXT-B-1_SO-1',
'desc': 'query the "bold" state, selection wrapping the bold tag',
'pad': 'foo[<b>bar</b>]baz',
'expected': True },
{ 'id': 'B_TEXT-B-1_SO-2',
'desc': 'query the "bold" state, selection wrapping the bold tag',
'pad': 'foo{<b>bar</b>}baz',
'expected': True },
{ 'id': 'B_TEXT-B-1_SL',
'desc': 'query the "bold" state, selection containing non-bold text',
'pad': 'fo[o<b>ba]r</b>baz',
'expected': False },
{ 'id': 'B_TEXT-B-1_SR',
'desc': 'query the "bold" state, selection containing non-bold text',
'pad': 'foo<b>b[ar</b>b]az',
'expected': False },
{ 'id': 'B_TEXT-B-1_SO-3',
'desc': 'query the "bold" state, selection containing non-bold text',
'pad': 'fo[o<b>bar</b>b]az',
'expected': False },
{ 'id': 'B_B.TEXT.B-1_SM',
'desc': 'query the "bold" state, selection including non-bold text',
'pad': '<b>fo[o</b>bar<b>b]az</b>',
'expected': False },
{ 'id': 'B_B.B.B-1_SM',
'desc': 'query the "bold" state, selection mixed, but all bold',
'pad': '<b>fo[o</b><b>bar</b><b>b]az</b>',
'expected': True },
{ 'id': 'B_B.STRONG.B-1_SM',
'desc': 'query the "bold" state, selection mixed, but all bold',
'pad': '<b>fo[o</b><strong>bar</strong><b>b]az</b>',
'expected': True },
{ 'id': 'B_SPAN.b.MYB.SPANs:fw:b-1_SM',
'desc': 'query the "bold" state, selection mixed, but all bold',
'pad': '<span class="b">fo[o</span><myb>bar</myb><span style="font-weight: bold">b]az</span>',
'expected': True }
]
},
{ 'desc': 'query italic state',
'qcstate': 'italic',
'tests': [
{ 'id': 'I_TEXT_SI',
'rte1-id': 'q-italic-0',
'desc': 'query the "italic" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'I_I-1_SI',
'rte1-id': 'q-italic-1',
'desc': 'query the "italic" state',
'pad': '<i>foo[bar]baz</i>',
'expected': True },
{ 'id': 'I_EM-1_SI',
'rte1-id': 'q-italic-2',
'desc': 'query the "italic" state',
'pad': '<em>foo[bar]baz</em>',
'expected': True },
{ 'id': 'I_SPANs:fs:i-1_SI',
'rte1-id': 'q-italic-3',
'desc': 'query the "italic" state',
'pad': '<span style="font-style: italic">foo[bar]baz</span>',
'expected': True },
{ 'id': 'I_SPANs:fs:n-1_SI',
'desc': 'query the "italic" state',
'pad': '<span style="font-style: normal">foo[bar]baz</span>',
'expected': False },
{ 'id': 'I_I-SPANs:fs:n-1_SI',
'rte1-id': 'q-italic-4',
'desc': 'query the "italic" state',
'pad': '<i><span style="font-style: normal">foo[bar]baz</span></i>',
'expected': False },
{ 'id': 'I_SPAN.i-1-SI',
'desc': 'query the "italic" state',
'pad': '<span class="i">foo[bar]baz</span>',
'expected': True },
{ 'id': 'I_MYI-1-SI',
'desc': 'query the "italic" state',
'pad': '<myi>foo[bar]baz</myi>',
'expected': True }
]
},
{ 'desc': 'query underline state',
'qcstate': 'underline',
'tests': [
{ 'id': 'U_TEXT_SI',
'rte1-id': 'q-underline-0',
'desc': 'query the "underline" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'U_U-1_SI',
'rte1-id': 'q-underline-1',
'desc': 'query the "underline" state',
'pad': '<u>foo[bar]baz</u>',
'expected': True },
{ 'id': 'U_Us:td:n-1_SI',
'rte1-id': 'q-underline-4',
'desc': 'query the "underline" state',
'pad': '<u style="text-decoration: none">foo[bar]baz</u>',
'expected': False },
{ 'id': 'U_Ah:url-1_SI',
'rte1-id': 'q-underline-2',
'desc': 'query the "underline" state',
'pad': '<a href="http://www.goo.gl">foo[bar]baz</a>',
'expected': True },
{ 'id': 'U_Ah:url.s:td:n-1_SI',
'rte1-id': 'q-underline-5',
'desc': 'query the "underline" state',
'pad': '<a href="http://www.goo.gl" style="text-decoration: none">foo[bar]baz</a>',
'expected': False },
{ 'id': 'U_SPANs:td:u-1_SI',
'rte1-id': 'q-underline-3',
'desc': 'query the "underline" state',
'pad': '<span style="text-decoration: underline">foo[bar]baz</span>',
'expected': True },
{ 'id': 'U_SPAN.u-1-SI',
'desc': 'query the "underline" state',
'pad': '<span class="u">foo[bar]baz</span>',
'expected': True },
{ 'id': 'U_MYU-1-SI',
'desc': 'query the "underline" state',
'pad': '<myu>foo[bar]baz</myu>',
'expected': True }
]
},
{ 'desc': 'query strike-through state',
'qcstate': 'strikethrough',
'tests': [
{ 'id': 'S_TEXT_SI',
'rte1-id': 'q-strikethrough-0',
'desc': 'query the "strikethrough" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'S_S-1_SI',
'rte1-id': 'q-strikethrough-3',
'desc': 'query the "strikethrough" state',
'pad': '<s>foo[bar]baz</s>',
'expected': True },
{ 'id': 'S_STRIKE-1_SI',
'rte1-id': 'q-strikethrough-1',
'desc': 'query the "strikethrough" state',
'pad': '<strike>foo[bar]baz</strike>',
'expected': True },
{ 'id': 'S_STRIKEs:td:n-1_SI',
'rte1-id': 'q-strikethrough-2',
'desc': 'query the "strikethrough" state',
'pad': '<strike style="text-decoration: none">foo[bar]baz</strike>',
'expected': False },
{ 'id': 'S_DEL-1_SI',
'rte1-id': 'q-strikethrough-4',
'desc': 'query the "strikethrough" state',
'pad': '<del>foo[bar]baz</del>',
'expected': True },
{ 'id': 'S_SPANs:td:lt-1_SI',
'rte1-id': 'q-strikethrough-5',
'desc': 'query the "strikethrough" state',
'pad': '<span style="text-decoration: line-through">foo[bar]baz</span>',
'expected': True },
{ 'id': 'S_SPAN.s-1-SI',
'desc': 'query the "strikethrough" state',
'pad': '<span class="s">foo[bar]baz</span>',
'expected': True },
{ 'id': 'S_MYS-1-SI',
'desc': 'query the "strikethrough" state',
'pad': '<mys>foo[bar]baz</mys>',
'expected': True },
{ 'id': 'S_S.STRIKE.DEL-1_SM',
'desc': 'query the "strikethrough" state, selection mixed, but all struck',
'pad': '<s>fo[o</s><strike>bar</strike><del>b]az</del>',
'expected': True }
]
},
{ 'desc': 'query subscript state',
'qcstate': 'subscript',
'tests': [
{ 'id': 'SUB_TEXT_SI',
'rte1-id': 'q-subscript-0',
'desc': 'query the "subscript" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'SUB_SUB-1_SI',
'rte1-id': 'q-subscript-1',
'desc': 'query the "subscript" state',
'pad': '<sub>foo[bar]baz</sub>',
'expected': True },
{ 'id': 'SUB_SPAN.sub-1-SI',
'desc': 'query the "subscript" state',
'pad': '<span class="sub">foo[bar]baz</span>',
'expected': True },
{ 'id': 'SUB_MYSUB-1-SI',
'desc': 'query the "subscript" state',
'pad': '<mysub>foo[bar]baz</mysub>',
'expected': True }
]
},
{ 'desc': 'query superscript state',
'qcstate': 'superscript',
'tests': [
{ 'id': 'SUP_TEXT_SI',
'rte1-id': 'q-superscript-0',
'desc': 'query the "superscript" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'SUP_SUP-1_SI',
'rte1-id': 'q-superscript-1',
'desc': 'query the "superscript" state',
'pad': '<sup>foo[bar]baz</sup>',
'expected': True },
{ 'id': 'IOL_TEXT_SI',
'desc': 'query the "insertorderedlist" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'SUP_SPAN.sup-1-SI',
'desc': 'query the "superscript" state',
'pad': '<span class="sup">foo[bar]baz</span>',
'expected': True },
{ 'id': 'SUP_MYSUP-1-SI',
'desc': 'query the "superscript" state',
'pad': '<mysup>foo[bar]baz</mysup>',
'expected': True }
]
},
{ 'desc': 'query whether the selection is in an ordered list',
'qcstate': 'insertorderedlist',
'tests': [
{ 'id': 'IOL_TEXT-1_SI',
'rte1-id': 'q-insertorderedlist-0',
'desc': 'query the "insertorderedlist" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'IOL_OL-LI-1_SI',
'rte1-id': 'q-insertorderedlist-1',
'desc': 'query the "insertorderedlist" state',
'pad': '<ol><li>foo[bar]baz</li></ol>',
'expected': True },
{ 'id': 'IOL_UL_LI-1_SI',
'rte1-id': 'q-insertorderedlist-2',
'desc': 'query the "insertorderedlist" state',
'pad': '<ul><li>foo[bar]baz</li></ul>',
'expected': False }
]
},
{ 'desc': 'query whether the selection is in an unordered list',
'qcstate': 'insertunorderedlist',
'tests': [
{ 'id': 'IUL_TEXT_SI',
'rte1-id': 'q-insertunorderedlist-0',
'desc': 'query the "insertunorderedlist" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'IUL_OL-LI-1_SI',
'rte1-id': 'q-insertunorderedlist-1',
'desc': 'query the "insertunorderedlist" state',
'pad': '<ol><li>foo[bar]baz</li></ol>',
'expected': False },
{ 'id': 'IUL_UL-LI-1_SI',
'rte1-id': 'q-insertunorderedlist-2',
'desc': 'query the "insertunorderedlist" state',
'pad': '<ul><li>foo[bar]baz</li></ul>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is centered',
'qcstate': 'justifycenter',
'tests': [
{ 'id': 'JC_TEXT_SI',
'rte1-id': 'q-justifycenter-0',
'desc': 'query the "justifycenter" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JC_DIVa:c-1_SI',
'rte1-id': 'q-justifycenter-1',
'desc': 'query the "justifycenter" state',
'pad': '<div align="center">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JC_Pa:c-1_SI',
'rte1-id': 'q-justifycenter-2',
'desc': 'query the "justifycenter" state',
'pad': '<p align="center">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JC_SPANs:ta:c-1_SI',
'rte1-id': 'q-justifycenter-3',
'desc': 'query the "justifycenter" state',
'pad': '<div style="text-align: center">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JC_SPAN.jc-1-SI',
'desc': 'query the "justifycenter" state',
'pad': '<span class="jc">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JC_MYJC-1-SI',
'desc': 'query the "justifycenter" state',
'pad': '<myjc>foo[bar]baz</myjc>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is justified',
'qcstate': 'justifyfull',
'tests': [
{ 'id': 'JF_TEXT_SI',
'rte1-id': 'q-justifyfull-0',
'desc': 'query the "justifyfull" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JF_DIVa:j-1_SI',
'rte1-id': 'q-justifyfull-1',
'desc': 'query the "justifyfull" state',
'pad': '<div align="justify">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JF_Pa:j-1_SI',
'rte1-id': 'q-justifyfull-2',
'desc': 'query the "justifyfull" state',
'pad': '<p align="justify">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JF_SPANs:ta:j-1_SI',
'rte1-id': 'q-justifyfull-3',
'desc': 'query the "justifyfull" state',
'pad': '<span style="text-align: justify">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JF_SPAN.jf-1-SI',
'desc': 'query the "justifyfull" state',
'pad': '<span class="jf">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JF_MYJF-1-SI',
'desc': 'query the "justifyfull" state',
'pad': '<myjf>foo[bar]baz</myjf>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is aligned left',
'qcstate': 'justifyleft',
'tests': [
{ 'id': 'JL_TEXT_SI',
'desc': 'query the "justifyleft" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JL_DIVa:l-1_SI',
'rte1-id': 'q-justifyleft-0',
'desc': 'query the "justifyleft" state',
'pad': '<div align="left">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JL_Pa:l-1_SI',
'rte1-id': 'q-justifyleft-1',
'desc': 'query the "justifyleft" state',
'pad': '<p align="left">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JL_SPANs:ta:l-1_SI',
'rte1-id': 'q-justifyleft-2',
'desc': 'query the "justifyleft" state',
'pad': '<span style="text-align: left">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JL_SPAN.jl-1-SI',
'desc': 'query the "justifyleft" state',
'pad': '<span class="jl">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JL_MYJL-1-SI',
'desc': 'query the "justifyleft" state',
'pad': '<myjl>foo[bar]baz</myjl>',
'expected': True }
]
},
{ 'desc': 'query whether the paragraph is aligned right',
'qcstate': 'justifyright',
'tests': [
{ 'id': 'JR_TEXT_SI',
'rte1-id': 'q-justifyright-0',
'desc': 'query the "justifyright" state',
'pad': 'foo[bar]baz',
'expected': False },
{ 'id': 'JR_DIVa:r-1_SI',
'rte1-id': 'q-justifyright-1',
'desc': 'query the "justifyright" state',
'pad': '<div align="right">foo[bar]baz</div>',
'expected': True },
{ 'id': 'JR_Pa:r-1_SI',
'rte1-id': 'q-justifyright-2',
'desc': 'query the "justifyright" state',
'pad': '<p align="right">foo[bar]baz</p>',
'expected': True },
{ 'id': 'JR_SPANs:ta:r-1_SI',
'rte1-id': 'q-justifyright-3',
'desc': 'query the "justifyright" state',
'pad': '<span style="text-align: right">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JR_SPAN.jr-1-SI',
'desc': 'query the "justifyright" state',
'pad': '<span class="jr">foo[bar]baz</span>',
'expected': True },
{ 'id': 'JR_MYJR-1-SI',
'desc': 'query the "justifyright" state',
'pad': '<myjr>foo[bar]baz</myjr>',
'expected': True }
]
}
]
}
QUERYSTATE_TESTS_CSS = {
'id': 'QSC',
'caption': 'queryCommandState Tests, using styleWithCSS',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': True,
'Proposed': QUERYSTATE_TESTS['Proposed']
}

View File

@ -0,0 +1,226 @@
QUERYSUPPORTED_TESTS = {
'id': 'Q',
'caption': 'queryCommandSupported Tests',
'pad': 'foo[bar]baz',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'expected': True,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': 'HTML5 commands',
'tests': [
{ 'id': 'SELECTALL_TEXT-1',
'desc': 'check whether the "selectall" command is supported',
'qcsupported': 'selectall' },
{ 'id': 'UNSELECT_TEXT-1',
'desc': 'check whether the "unselect" command is supported',
'qcsupported': 'unselect' },
{ 'id': 'UNDO_TEXT-1',
'desc': 'check whether the "undo" command is supported',
'qcsupported': 'undo' },
{ 'id': 'REDO_TEXT-1',
'desc': 'check whether the "redo" command is supported',
'qcsupported': 'redo' },
{ 'id': 'BOLD_TEXT-1',
'desc': 'check whether the "bold" command is supported',
'qcsupported': 'bold' },
{ 'id': 'BOLD_B',
'desc': 'check whether the "bold" command is supported',
'qcsupported': 'bold',
'pad': '<b>foo[bar]baz</b>' },
{ 'id': 'ITALIC_TEXT-1',
'desc': 'check whether the "italic" command is supported',
'qcsupported': 'italic' },
{ 'id': 'ITALIC_I',
'desc': 'check whether the "italic" command is supported',
'qcsupported': 'italic',
'pad': '<i>foo[bar]baz</i>' },
{ 'id': 'UNDERLINE_TEXT-1',
'desc': 'check whether the "underline" command is supported',
'qcsupported': 'underline' },
{ 'id': 'STRIKETHROUGH_TEXT-1',
'desc': 'check whether the "strikethrough" command is supported',
'qcsupported': 'strikethrough' },
{ 'id': 'SUBSCRIPT_TEXT-1',
'desc': 'check whether the "subscript" command is supported',
'qcsupported': 'subscript' },
{ 'id': 'SUPERSCRIPT_TEXT-1',
'desc': 'check whether the "superscript" command is supported',
'qcsupported': 'superscript' },
{ 'id': 'FORMATBLOCK_TEXT-1',
'desc': 'check whether the "formatblock" command is supported',
'qcsupported': 'formatblock' },
{ 'id': 'CREATELINK_TEXT-1',
'desc': 'check whether the "createlink" command is supported',
'qcsupported': 'createlink' },
{ 'id': 'UNLINK_TEXT-1',
'desc': 'check whether the "unlink" command is supported',
'qcsupported': 'unlink' },
{ 'id': 'INSERTHTML_TEXT-1',
'desc': 'check whether the "inserthtml" command is supported',
'qcsupported': 'inserthtml' },
{ 'id': 'INSERTHORIZONTALRULE_TEXT-1',
'desc': 'check whether the "inserthorizontalrule" command is supported',
'qcsupported': 'inserthorizontalrule' },
{ 'id': 'INSERTIMAGE_TEXT-1',
'desc': 'check whether the "insertimage" command is supported',
'qcsupported': 'insertimage' },
{ 'id': 'INSERTLINEBREAK_TEXT-1',
'desc': 'check whether the "insertlinebreak" command is supported',
'qcsupported': 'insertlinebreak' },
{ 'id': 'INSERTPARAGRAPH_TEXT-1',
'desc': 'check whether the "insertparagraph" command is supported',
'qcsupported': 'insertparagraph' },
{ 'id': 'INSERTORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertorderedlist" command is supported',
'qcsupported': 'insertorderedlist' },
{ 'id': 'INSERTUNORDEREDLIST_TEXT-1',
'desc': 'check whether the "insertunorderedlist" command is supported',
'qcsupported': 'insertunorderedlist' },
{ 'id': 'INSERTTEXT_TEXT-1',
'desc': 'check whether the "inserttext" command is supported',
'qcsupported': 'inserttext' },
{ 'id': 'DELETE_TEXT-1',
'desc': 'check whether the "delete" command is supported',
'qcsupported': 'delete' },
{ 'id': 'FORWARDDELETE_TEXT-1',
'desc': 'check whether the "forwarddelete" command is supported',
'qcsupported': 'forwarddelete' }
]
},
{ 'desc': 'MIDAS commands',
'tests': [
{ 'id': 'STYLEWITHCSS_TEXT-1',
'desc': 'check whether the "styleWithCSS" command is supported',
'qcsupported': 'styleWithCSS' },
{ 'id': 'CONTENTREADONLY_TEXT-1',
'desc': 'check whether the "contentreadonly" command is supported',
'qcsupported': 'contentreadonly' },
{ 'id': 'BACKCOLOR_TEXT-1',
'desc': 'check whether the "backcolor" command is supported',
'qcsupported': 'backcolor' },
{ 'id': 'FORECOLOR_TEXT-1',
'desc': 'check whether the "forecolor" command is supported',
'qcsupported': 'forecolor' },
{ 'id': 'HILITECOLOR_TEXT-1',
'desc': 'check whether the "hilitecolor" command is supported',
'qcsupported': 'hilitecolor' },
{ 'id': 'FONTNAME_TEXT-1',
'desc': 'check whether the "fontname" command is supported',
'qcsupported': 'fontname' },
{ 'id': 'FONTSIZE_TEXT-1',
'desc': 'check whether the "fontsize" command is supported',
'qcsupported': 'fontsize' },
{ 'id': 'INCREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "increasefontsize" command is supported',
'qcsupported': 'increasefontsize' },
{ 'id': 'DECREASEFONTSIZE_TEXT-1',
'desc': 'check whether the "decreasefontsize" command is supported',
'qcsupported': 'decreasefontsize' },
{ 'id': 'HEADING_TEXT-1',
'desc': 'check whether the "heading" command is supported',
'qcsupported': 'heading' },
{ 'id': 'INDENT_TEXT-1',
'desc': 'check whether the "indent" command is supported',
'qcsupported': 'indent' },
{ 'id': 'OUTDENT_TEXT-1',
'desc': 'check whether the "outdent" command is supported',
'qcsupported': 'outdent' },
{ 'id': 'CREATEBOOKMARK_TEXT-1',
'desc': 'check whether the "createbookmark" command is supported',
'qcsupported': 'createbookmark' },
{ 'id': 'UNBOOKMARK_TEXT-1',
'desc': 'check whether the "unbookmark" command is supported',
'qcsupported': 'unbookmark' },
{ 'id': 'JUSTIFYCENTER_TEXT-1',
'desc': 'check whether the "justifycenter" command is supported',
'qcsupported': 'justifycenter' },
{ 'id': 'JUSTIFYFULL_TEXT-1',
'desc': 'check whether the "justifyfull" command is supported',
'qcsupported': 'justifyfull' },
{ 'id': 'JUSTIFYLEFT_TEXT-1',
'desc': 'check whether the "justifyleft" command is supported',
'qcsupported': 'justifyleft' },
{ 'id': 'JUSTIFYRIGHT_TEXT-1',
'desc': 'check whether the "justifyright" command is supported',
'qcsupported': 'justifyright' },
{ 'id': 'REMOVEFORMAT_TEXT-1',
'desc': 'check whether the "removeformat" command is supported',
'qcsupported': 'removeformat' },
{ 'id': 'COPY_TEXT-1',
'desc': 'check whether the "copy" command is supported',
'qcsupported': 'copy' },
{ 'id': 'CUT_TEXT-1',
'desc': 'check whether the "cut" command is supported',
'qcsupported': 'cut' },
{ 'id': 'PASTE_TEXT-1',
'desc': 'check whether the "paste" command is supported',
'qcsupported': 'paste' }
]
},
{ 'desc': 'Other tests',
'tests': [
{ 'id': 'garbage-1_TEXT-1',
'desc': 'check correct return value with garbage input',
'qcsupported': '#!#@7',
'expected': False }
]
}
]
}

View File

@ -0,0 +1,429 @@
QUERYVALUE_TESTS = {
'id': 'QV',
'caption': 'queryCommandValue Tests',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'tests': [
]
},
{ 'desc': '[HTML5] query bold value',
'qcvalue': 'bold',
'tests': [
{ 'id': 'B_TEXT_SI',
'desc': 'query the "bold" value',
'pad': 'foo[bar]baz',
'expected': 'false' },
{ 'id': 'B_B-1_SI',
'desc': 'query the "bold" value',
'pad': '<b>foo[bar]baz</b>',
'expected': 'true' },
{ 'id': 'B_STRONG-1_SI',
'desc': 'query the "bold" value',
'pad': '<strong>foo[bar]baz</strong>',
'expected': 'true' },
{ 'id': 'B_SPANs:fw:b-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-weight: bold">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'B_SPANs:fw:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-weight: normal">foo[bar]baz</span>',
'expected': 'false' },
{ 'id': 'B_Bs:fw:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<b><span style="font-weight: normal">foo[bar]baz</span></b>',
'expected': 'false' },
{ 'id': 'B_SPAN.b-1_SI',
'desc': 'query the "bold" value',
'pad': '<span class="b">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'B_MYB-1-SI',
'desc': 'query the "bold" value',
'pad': '<myb>foo[bar]baz</myb>',
'expected': 'true' }
]
},
{ 'desc': '[HTML5] query italic value',
'qcvalue': 'italic',
'tests': [
{ 'id': 'I_TEXT_SI',
'desc': 'query the "bold" value',
'pad': 'foo[bar]baz',
'expected': 'false' },
{ 'id': 'I_I-1_SI',
'desc': 'query the "bold" value',
'pad': '<i>foo[bar]baz</i>',
'expected': 'true' },
{ 'id': 'I_EM-1_SI',
'desc': 'query the "bold" value',
'pad': '<em>foo[bar]baz</em>',
'expected': 'true' },
{ 'id': 'I_SPANs:fs:i-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-style: italic">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'I_SPANs:fs:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<span style="font-style: normal">foo[bar]baz</span>',
'expected': 'false' },
{ 'id': 'I_I-SPANs:fs:n-1_SI',
'desc': 'query the "bold" value',
'pad': '<i><span style="font-style: normal">foo[bar]baz</span></i>',
'expected': 'false' },
{ 'id': 'I_SPAN.i-1_SI',
'desc': 'query the "italic" value',
'pad': '<span class="i">foo[bar]baz</span>',
'expected': 'true' },
{ 'id': 'I_MYI-1-SI',
'desc': 'query the "italic" value',
'pad': '<myi>foo[bar]baz</myi>',
'expected': 'true' }
]
},
{ 'desc': '[HTML5] query block formatting value',
'qcvalue': 'formatblock',
'tests': [
{ 'id': 'FB_TEXT-1_SC',
'desc': 'query the "formatBlock" value',
'pad': 'foobar^baz',
'expected': '',
'accept': 'normal' },
{ 'id': 'FB_H1-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<h1>foobar^baz</h1>',
'expected': 'h1' },
{ 'id': 'FB_PRE-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<pre>foobar^baz</pre>',
'expected': 'pre' },
{ 'id': 'FB_BQ-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<blockquote>foobar^baz</blockquote>',
'expected': 'blockquote' },
{ 'id': 'FB_ADDRESS-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<address>foobar^baz</address>',
'expected': 'address' },
{ 'id': 'FB_H1-H2-1_SC',
'desc': 'query the "formatBlock" value',
'pad': '<h1>foo<h2>ba^r</h2>baz</h1>',
'expected': 'h2' },
{ 'id': 'FB_H1-H2-1_SL',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': '<h1>fo[o<h2>ba]r</h2>baz</h1>',
'expected': 'h1' },
{ 'id': 'FB_H1-H2-1_SR',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': '<h1>foo<h2>b[ar</h2>ba]z</h1>',
'expected': 'h1' },
{ 'id': 'FB_TEXT-ADDRESS-1_SL',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': 'fo[o<ADDRESS>ba]r</ADDRESS>baz',
'expected': '',
'accept': 'normal' },
{ 'id': 'FB_TEXT-ADDRESS-1_SR',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': 'foo<ADDRESS>b[ar</ADDRESS>ba]z',
'expected': '',
'accept': 'normal' },
{ 'id': 'FB_H1-H2.TEXT.H2-1_SM',
'desc': 'query the "formatBlock" value on oblique selection (outermost formatting expected)',
'pad': '<h1><h2>fo[o</h2>bar<h2>b]az</h2></h1>',
'expected': 'h1' }
]
},
{ 'desc': '[MIDAS] query heading type',
'qcvalue': 'heading',
'tests': [
{ 'id': 'H_H1-1_SC',
'desc': 'query the "heading" type',
'pad': '<h1>foobar^baz</h1>',
'expected': 'h1',
'accept': '<h1>' },
{ 'id': 'H_H3-1_SC',
'desc': 'query the "heading" type',
'pad': '<h3>foobar^baz</h3>',
'expected': 'h3',
'accept': '<h3>' },
{ 'id': 'H_H1-H2-H3-H4-1_SC',
'desc': 'query the "heading" type within nested heading tags',
'pad': '<h1><h2><h3><h4>foobar^baz</h4></h3></h2></h1>',
'expected': 'h4',
'accept': '<h4>' },
{ 'id': 'H_P-1_SC',
'desc': 'query the "heading" type outside of a heading',
'pad': '<p>foobar^baz</p>',
'expected': '' }
]
},
{ 'desc': '[MIDAS] query font name',
'qcvalue': 'fontname',
'tests': [
{ 'id': 'FN_FONTf:a-1_SI',
'rte1-id': 'q-fontname-0',
'desc': 'query the "fontname" value',
'pad': '<font face="arial">foo[bar]baz</font>',
'expected': 'arial' },
{ 'id': 'FN_SPANs:ff:a-1_SI',
'rte1-id': 'q-fontname-1',
'desc': 'query the "fontname" value',
'pad': '<span style="font-family: arial">foo[bar]baz</span>',
'expected': 'arial' },
{ 'id': 'FN_FONTf:a.s:ff:c-1_SI',
'rte1-id': 'q-fontname-2',
'desc': 'query the "fontname" value',
'pad': '<font face="arial" style="font-family: courier">foo[bar]baz</font>',
'expected': 'courier' },
{ 'id': 'FN_FONTf:a-FONTf:c-1_SI',
'rte1-id': 'q-fontname-3',
'desc': 'query the "fontname" value',
'pad': '<font face="arial"><font face="courier">foo[bar]baz</font></font>',
'expected': 'courier' },
{ 'id': 'FN_SPANs:ff:c-FONTf:a-1_SI',
'rte1-id': 'q-fontname-4',
'desc': 'query the "fontname" value',
'pad': '<span style="font-family: courier"><font face="arial">foo[bar]baz</font></span>',
'expected': 'arial' },
{ 'id': 'FN_SPAN.fs18px-1_SI',
'desc': 'query the "fontname" value',
'pad': '<span class="courier">foo[bar]baz</span>',
'expected': 'courier' },
{ 'id': 'FN_MYCOURIER-1-SI',
'desc': 'query the "fontname" value',
'pad': '<mycourier>foo[bar]baz</mycourier>',
'expected': 'courier' }
]
},
{ 'desc': '[MIDAS] query font size',
'qcvalue': 'fontsize',
'tests': [
{ 'id': 'FS_FONTsz:4-1_SI',
'rte1-id': 'q-fontsize-0',
'desc': 'query the "fontsize" value',
'pad': '<font size=4>foo[bar]baz</font>',
'expected': '18px' },
{ 'id': 'FS_FONTs:fs:l-1_SI',
'desc': 'query the "fontsize" value',
'pad': '<font style="font-size: large">foo[bar]baz</font>',
'expected': '18px' },
{ 'id': 'FS_FONT.ass.s:fs:l-1_SI',
'rte1-id': 'q-fontsize-1',
'desc': 'query the "fontsize" value',
'pad': '<font class="Apple-style-span" style="font-size: large">foo[bar]baz</font>',
'expected': '18px' },
{ 'id': 'FS_FONTsz:1.s:fs:xl-1_SI',
'rte1-id': 'q-fontsize-2',
'desc': 'query the "fontsize" value',
'pad': '<font size=1 style="font-size: x-large">foo[bar]baz</font>',
'expected': '24px' },
{ 'id': 'FS_SPAN.large-1_SI',
'desc': 'query the "fontsize" value',
'pad': '<span class="large">foo[bar]baz</span>',
'expected': 'large' },
{ 'id': 'FS_SPAN.fs18px-1_SI',
'desc': 'query the "fontsize" value',
'pad': '<span class="fs18px">foo[bar]baz</span>',
'expected': '18px' },
{ 'id': 'FA_MYLARGE-1-SI',
'desc': 'query the "fontsize" value',
'pad': '<mylarge>foo[bar]baz</mylarge>',
'expected': 'large' },
{ 'id': 'FA_MYFS18PX-1-SI',
'desc': 'query the "fontsize" value',
'pad': '<myfs18px>foo[bar]baz</myfs18px>',
'expected': '18px' }
]
},
{ 'desc': '[MIDAS] query background color',
'qcvalue': 'backcolor',
'tests': [
{ 'id': 'BC_FONTs:bc:fca-1_SI',
'rte1-id': 'q-backcolor-0',
'desc': 'query the "backcolor" value',
'pad': '<font style="background-color: #ffccaa">foo[bar]baz</font>',
'expected': '#ffccaa' },
{ 'id': 'BC_SPANs:bc:abc-1_SI',
'rte1-id': 'q-backcolor-2',
'desc': 'query the "backcolor" value',
'pad': '<span style="background-color: #aabbcc">foo[bar]baz</span>',
'expected': '#aabbcc' },
{ 'id': 'BC_FONTs:bc:084-SPAN-1_SI',
'desc': 'query the "backcolor" value, where the color was set on an ancestor',
'pad': '<font style="background-color: #008844"><span>foo[bar]baz</span></font>',
'expected': '#008844' },
{ 'id': 'BC_SPANs:bc:cde-SPAN-1_SI',
'desc': 'query the "backcolor" value, where the color was set on an ancestor',
'pad': '<span style="background-color: #ccddee"><span>foo[bar]baz</span></span>',
'expected': '#ccddee' },
{ 'id': 'BC_SPAN.ass.s:bc:rgb-1_SI',
'rte1-id': 'q-backcolor-1',
'desc': 'query the "backcolor" value',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">foo[bar]baz</span>',
'expected': '#ff0000' },
{ 'id': 'BC_SPAN.bcred-1_SI',
'desc': 'query the "backcolor" value',
'pad': '<span class="bcred">foo[bar]baz</span>',
'expected': 'red' },
{ 'id': 'BC_MYBCRED-1-SI',
'desc': 'query the "backcolor" value',
'pad': '<mybcred>foo[bar]baz</mybcred>',
'expected': 'red' }
]
},
{ 'desc': '[MIDAS] query text color',
'qcvalue': 'forecolor',
'tests': [
{ 'id': 'FC_FONTc:f00-1_SI',
'rte1-id': 'q-forecolor-0',
'desc': 'query the "forecolor" value',
'pad': '<font color="#ff0000">foo[bar]baz</font>',
'expected': '#ff0000' },
{ 'id': 'FC_SPANs:c:0f0-1_SI',
'rte1-id': 'q-forecolor-1',
'desc': 'query the "forecolor" value',
'pad': '<span style="color: #00ff00">foo[bar]baz</span>',
'expected': '#00ff00' },
{ 'id': 'FC_FONTc:333.s:c:999-1_SI',
'rte1-id': 'q-forecolor-2',
'desc': 'query the "forecolor" value',
'pad': '<font color="#333333" style="color: #999999">foo[bar]baz</font>',
'expected': '#999999' },
{ 'id': 'FC_FONTc:641-SPAN-1_SI',
'desc': 'query the "forecolor" value, where the color was set on an ancestor',
'pad': '<font color="#664411"><span>foo[bar]baz</span></font>',
'expected': '#664411' },
{ 'id': 'FC_SPANs:c:d95-SPAN-1_SI',
'desc': 'query the "forecolor" value, where the color was set on an ancestor',
'pad': '<span style="color: #dd9955"><span>foo[bar]baz</span></span>',
'expected': '#dd9955' },
{ 'id': 'FC_SPAN.red-1_SI',
'desc': 'query the "forecolor" value',
'pad': '<span class="red">foo[bar]baz</span>',
'expected': 'red' },
{ 'id': 'FC_MYRED-1-SI',
'desc': 'query the "forecolor" value',
'pad': '<myred>foo[bar]baz</myred>',
'expected': 'red' }
]
},
{ 'desc': '[MIDAS] query hilight color (same as background color)',
'qcvalue': 'hilitecolor',
'tests': [
{ 'id': 'HC_FONTs:bc:fc0-1_SI',
'rte1-id': 'q-hilitecolor-0',
'desc': 'query the "hilitecolor" value',
'pad': '<font style="background-color: #ffcc00">foo[bar]baz</font>',
'expected': '#ffcc00' },
{ 'id': 'HC_SPANs:bc:a0c-1_SI',
'rte1-id': 'q-hilitecolor-2',
'desc': 'query the "hilitecolor" value',
'pad': '<span style="background-color: #aa00cc">foo[bar]baz</span>',
'expected': '#aa00cc' },
{ 'id': 'HC_SPAN.ass.s:bc:rgb-1_SI',
'rte1-id': 'q-hilitecolor-1',
'desc': 'query the "hilitecolor" value',
'pad': '<span class="Apple-style-span" style="background-color: rgb(255, 0, 0)">foo[bar]baz</span>',
'expected': '#ff0000' },
{ 'id': 'HC_FONTs:bc:83e-SPAN-1_SI',
'desc': 'query the "hilitecolor" value, where the color was set on an ancestor',
'pad': '<font style="background-color: #8833ee"><span>foo[bar]baz</span></font>',
'expected': '#8833ee' },
{ 'id': 'HC_SPANs:bc:b12-SPAN-1_SI',
'desc': 'query the "hilitecolor" value, where the color was set on an ancestor',
'pad': '<span style="background-color: #bb1122"><span>foo[bar]baz</span></span>',
'expected': '#bb1122' },
{ 'id': 'HC_SPAN.bcred-1_SI',
'desc': 'query the "hilitecolor" value',
'pad': '<span class="bcred">foo[bar]baz</span>',
'expected': 'red' },
{ 'id': 'HC_MYBCRED-1-SI',
'desc': 'query the "hilitecolor" value',
'pad': '<mybcred>foo[bar]baz</mybcred>',
'expected': 'red' }
]
}
]
}
QUERYVALUE_TESTS_CSS = {
'id': 'QVC',
'caption': 'queryCommandValue Tests, using styleWithCSS',
'checkAttrs': False,
'checkStyle': False,
'styleWithCSS': True,
'Proposed': QUERYVALUE_TESTS['Proposed']
}

View File

@ -0,0 +1,772 @@
SELECTION_TESTS = {
'id': 'S',
'caption': 'Selection Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'selectall',
'command': 'selectall',
'tests': [
{ 'id': 'SELALL_TEXT-1_SI',
'desc': 'select all, text only',
'pad': 'foo [bar] baz',
'expected': [ '[foo bar baz]',
'{foo bar baz}' ] },
{ 'id': 'SELALL_I-1_SI',
'desc': 'select all, with outer tags',
'pad': '<i>foo [bar] baz</i>',
'expected': '{<i>foo bar baz</i>}' }
]
},
{ 'desc': 'unselect',
'command': 'unselect',
'tests': [
{ 'id': 'UNSEL_TEXT-1_SI',
'desc': 'unselect',
'pad': 'foo [bar] baz',
'expected': 'foo bar baz' }
]
},
{ 'desc': 'sel.modify (generic)',
'tests': [
{ 'id': 'SM:m.f.c_TEXT-1_SC-1',
'desc': 'move caret 1 character forward',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo b^ar baz',
'expected': 'foo ba^r baz' },
{ 'id': 'SM:m.b.c_TEXT-1_SC-1',
'desc': 'move caret 1 character backward',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo b^ar baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.f.c_TEXT-1_SI-1',
'desc': 'move caret forward (sollapse selection)',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo [bar] baz',
'expected': 'foo bar^ baz' },
{ 'id': 'SM:m.b.c_TEXT-1_SI-1',
'desc': 'move caret backward (collapse selection)',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo [bar] baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.f.w_TEXT-1_SC-1',
'desc': 'move caret 1 word forward',
'function': 'sel.modify("move", "forward", "word");',
'pad': 'foo b^ar baz',
'expected': 'foo bar^ baz' },
{ 'id': 'SM:m.f.w_TEXT-1_SC-2',
'desc': 'move caret 1 word forward',
'function': 'sel.modify("move", "forward", "word");',
'pad': 'foo^ bar baz',
'expected': 'foo bar^ baz' },
{ 'id': 'SM:m.f.w_TEXT-1_SI-1',
'desc': 'move caret 1 word forward from non-collapsed selection',
'function': 'sel.modify("move", "forward", "word");',
'pad': 'foo [bar] baz',
'expected': 'foo bar baz^' },
{ 'id': 'SM:m.b.w_TEXT-1_SC-1',
'desc': 'move caret 1 word backward',
'function': 'sel.modify("move", "backward", "word");',
'pad': 'foo b^ar baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.b.w_TEXT-1_SC-3',
'desc': 'move caret 1 word backward',
'function': 'sel.modify("move", "backward", "word");',
'pad': 'foo bar ^baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:m.b.w_TEXT-1_SI-1',
'desc': 'move caret 1 word backward from non-collapsed selection',
'function': 'sel.modify("move", "backward", "word");',
'pad': 'foo [bar] baz',
'expected': '^foo bar baz' }
]
},
{ 'desc': 'sel.modify: move forward over combining diacritics, etc.',
'tests': [
{ 'id': 'SM:m.f.c_CHAR-2_SC-1',
'desc': 'move 1 character forward over combined o with diaeresis',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^&#xF6;barbaz',
'expected': 'fo&#xF6;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-3_SC-1',
'desc': 'move 1 character forward over character with combining diaeresis above',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0308;barbaz',
'expected': 'foo&#x0308;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-4_SC-1',
'desc': 'move 1 character forward over character with combining diaeresis below',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0324;barbaz',
'expected': 'foo&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SC-1',
'desc': 'move 1 character forward over character with combining diaeresis above and below',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0308;&#x0324;barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SI-1',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection on diaeresis above',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo[&#x0308;]&#x0324;barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SI-2',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection on diaeresis below',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo&#x0308;[&#x0324;]barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SL',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection oblique on diaeresis and preceding text',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo[o&#x0308;]&#x0324;barbaz',
'expected': 'foo&#x0308;&#x0324;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-5_SR',
'desc': 'move 1 character forward over character with combining diaeresis above and below, selection oblique on diaeresis and following text',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo&#x0308;[&#x0324;bar]baz',
'expected': 'foo&#x0308;&#x0324;bar^baz' },
{ 'id': 'SM:m.f.c_CHAR-6_SC-1',
'desc': 'move 1 character forward over character with enclosing square',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x20DE;barbaz',
'expected': 'foo&#x20DE;^barbaz' },
{ 'id': 'SM:m.f.c_CHAR-7_SC-1',
'desc': 'move 1 character forward over character with combining long solidus overlay',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'fo^o&#x0338;barbaz',
'expected': 'foo&#x0338;^barbaz' }
]
},
{ 'desc': 'sel.modify: move backward over combining diacritics, etc.',
'tests': [
{ 'id': 'SM:m.b.c_CHAR-2_SC-1',
'desc': 'move 1 character backward over combined o with diaeresis',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'fo&#xF6;^barbaz',
'expected': 'fo^&#xF6;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-3_SC-1',
'desc': 'move 1 character backward over character with combining diaeresis above',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;^barbaz',
'expected': 'fo^o&#x0308;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-4_SC-1',
'desc': 'move 1 character backward over character with combining diaeresis below',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0324;^barbaz',
'expected': 'fo^o&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SC-1',
'desc': 'move 1 character backward over character with combining diaeresis above and below',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;&#x0324;^barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SI-1',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection on diaeresis above',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo[&#x0308;]&#x0324;barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SI-2',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection on diaeresis below',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;[&#x0324;]barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SL',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection oblique on diaeresis and preceding text',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'fo[o&#x0308;]&#x0324;barbaz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-5_SR',
'desc': 'move 1 character backward over character with combining diaeresis above and below, selection oblique on diaeresis and following text',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0308;[&#x0324;bar]baz',
'expected': 'fo^o&#x0308;&#x0324;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-6_SC-1',
'desc': 'move 1 character backward over character with enclosing square',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x20DE;^barbaz',
'expected': 'fo^o&#x20DE;barbaz' },
{ 'id': 'SM:m.b.c_CHAR-7_SC-1',
'desc': 'move 1 character backward over character with combining long solidus overlay',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo&#x0338;^barbaz',
'expected': 'fo^o&#x0338;barbaz' }
]
},
{ 'desc': 'sel.modify: move forward/backward/left/right in RTL text',
'tests': [
{ 'id': 'SM:m.f.c_Pdir:rtl-1_SC-1',
'desc': 'move caret forward 1 character in right-to-left text',
'function': 'sel.modify("move", "forward", "character");',
'pad': '<p dir="rtl">foo b^ar baz</p>',
'expected': '<p dir="rtl">foo ba^r baz</p>' },
{ 'id': 'SM:m.b.c_Pdir:rtl-1_SC-1',
'desc': 'move caret backward 1 character in right-to-left text',
'function': 'sel.modify("move", "backward", "character");',
'pad': '<p dir="rtl">foo ba^r baz</p>',
'expected': '<p dir="rtl">foo b^ar baz</p>' },
{ 'id': 'SM:m.r.c_Pdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the right in LTR text within RTL context',
'function': 'sel.modify("move", "right", "character");',
'pad': '<p dir="rtl">foo b^ar baz</p>',
'expected': '<p dir="rtl">foo ba^r baz</p>' },
{ 'id': 'SM:m.l.c_Pdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the left in LTR text within RTL context',
'function': 'sel.modify("move", "left", "character");',
'pad': '<p dir="rtl">foo ba^r baz</p>',
'expected': '<p dir="rtl">foo b^ar baz</p>' },
{ 'id': 'SM:m.f.c_TEXT:ar-1_SC-1',
'desc': 'move caret forward 1 character in Arabic text',
'function': 'sel.modify("move", "forward", "character");',
'pad': '&#1605;&#x0631;&#1581;^&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;',
'expected': '&#1605;&#x0631;&#1581;&#1576;^&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;' },
{ 'id': 'SM:m.b.c_TEXT:ar-1_SC-1',
'desc': 'move caret backward 1 character in Arabic text',
'function': 'sel.modify("move", "backward", "character");',
'pad': '&#1605;&#x0631;&#1581;^&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;',
'expected': '&#1605;&#x0631;^&#1581;&#1576;&#x0627; &#x0627;&#1604;&#x0639;&#x0627;&#1604;&#1605;' },
{ 'id': 'SM:m.f.c_TEXT:he-1_SC-1',
'desc': 'move caret forward 1 character in Hebrew text',
'function': 'sel.modify("move", "forward", "character");',
'pad': '&#x05E9;&#x05DC;^&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;',
'expected': '&#x05E9;&#x05DC;&#x05D5;^&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;' },
{ 'id': 'SM:m.b.c_TEXT:he-1_SC-1',
'desc': 'move caret backward 1 character in Hebrew text',
'function': 'sel.modify("move", "backward", "character");',
'pad': '&#x05E9;&#x05DC;^&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;',
'expected': '&#x05E9;^&#x05DC;&#x05D5;&#x05DD; &#x05E2;&#x05D5;&#x05DC;&#x05DD;' },
{ 'id': 'SM:m.f.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret forward 1 character inside <bdo>',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'foo <bdo dir="rtl">b^ar</bdo> baz',
'expected': 'foo <bdo dir="rtl">ba^r</bdo> baz' },
{ 'id': 'SM:m.b.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret backward 1 character inside <bdo>',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'foo <bdo dir="rtl">ba^r</bdo> baz',
'expected': 'foo <bdo dir="rtl">b^ar</bdo> baz' },
{ 'id': 'SM:m.r.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the right inside <bdo>',
'function': 'sel.modify("move", "right", "character");',
'pad': 'foo <bdo dir="rtl">ba^r</bdo> baz',
'expected': 'foo <bdo dir="rtl">b^ar</bdo> baz' },
{ 'id': 'SM:m.l.c_BDOdir:rtl-1_SC-1',
'desc': 'move caret 1 character to the left inside <bdo>',
'function': 'sel.modify("move", "left", "character");',
'pad': 'foo <bdo dir="rtl">b^ar</bdo> baz',
'expected': 'foo <bdo dir="rtl">ba^r</bdo> baz' },
{ 'id': 'SM:m.f.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret forward in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret backward in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret 1 character to the right in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrle-1_SC-rtl-1',
'desc': 'move caret 1 character to the left in RTL text within RLE-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret forward in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret backward in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret 1 character to the right in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrle-1_SC-ltr-1',
'desc': 'move caret 1 character to the left in LTR text within RLE-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLE)&#x202B;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLE)&#x202B;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret forward in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret backward in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret 1 character to the right in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;^&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrlo-1_SC-rtl-1',
'desc': 'move caret 1 character to the left in RTL text within RLO-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;^&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;car &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;^&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret forward in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.b.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret backward in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.r.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret 1 character to the right in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.l.c_TEXTrlo-1_SC-ltr-1',
'desc': 'move caret 1 character to the left in Latin text within RLO-PDF marks',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "(RLO)&#x202E;c^ar &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".',
'expected': 'I said, "(RLO)&#x202E;ca^r &#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;&#x202C;(PDF)".' },
{ 'id': 'SM:m.f.c_TEXTrlm-1_SC-1',
'desc': 'move caret forward in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "forward", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".' },
{ 'id': 'SM:m.b.c_TEXTrlm-1_SC-1',
'desc': 'move caret backward in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "backward", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".' },
{ 'id': 'SM:m.r.c_TEXTrlm-1_SC-1',
'desc': 'move caret 1 character to the right in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "right", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".' },
{ 'id': 'SM:m.l.c_TEXTrlm-1_SC-1',
'desc': 'move caret 1 character to the left in RTL text within neutral characters followed by RLM',
'function': 'sel.modify("move", "left", "character");',
'pad': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!^?!&#x200F;(RLM)".',
'expected': 'I said, "&#x064A;&#x0639;&#x0646;&#x064A; &#x0633;&#x064A;&#x0627;&#x0631;&#x0629;!?^!&#x200F;(RLM)".' }
]
},
{ 'desc': 'sel.modify: move forward/backward over words in Japanese text',
'tests': [
{ 'id': 'SM:m.f.w_TEXT-jp_SC-1',
'desc': 'move caret forward 1 word in Japanese text (adjective)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '^&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-2',
'desc': 'move caret forward 1 word in Japanese text (in the middle of a word)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;^&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-3',
'desc': 'move caret forward 1 word in Japanese text (noun)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;&#x767D;&#x3044;^&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': [ '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;^&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;^&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' ] },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-4',
'desc': 'move caret forward 1 word in Japanese text (Katakana)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;^&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;^&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;' },
{ 'id': 'SM:m.f.w_TEXT-jp_SC-5',
'desc': 'move caret forward 1 word in Japanese text (verb)',
'function': 'sel.modify("move", "forward", "word");',
'pad': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;^&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;&#x3002;',
'expected': '&#x9762;&#x767D;&#x3044;&#x4F8B;&#x6587;&#x3092;&#x30C6;&#x30B9;&#x30C8;&#x3057;&#x307E;&#x3057;&#x3087;&#x3046;^&#x3002;' }
]
},
{ 'desc': 'sel.modify: extend selection forward',
'tests': [
{ 'id': 'SM:e.f.c_TEXT-1_SC-1',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo ^bar baz',
'expected': 'foo [b]ar baz' },
{ 'id': 'SM:e.f.c_TEXT-1_SI-1',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo [b]ar baz',
'expected': 'foo [ba]r baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SC-1',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo ^bar baz',
'expected': 'foo [bar] baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SI-1',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo [b]ar baz',
'expected': 'foo [bar] baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SI-2',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo [bar] baz',
'expected': 'foo [bar baz]' }
]
},
{ 'desc': 'sel.modify: extend selection backward, shrinking it',
'tests': [
{ 'id': 'SM:e.b.c_TEXT-1_SI-2',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo [bar] baz',
'expected': 'foo [ba]r baz' },
{ 'id': 'SM:e.b.c_TEXT-1_SI-1',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo [b]ar baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-3',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo [bar baz]',
'expected': 'foo [bar] baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-2',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo [bar] baz',
'expected': 'foo ^bar baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-4',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo b[ar baz]',
'expected': 'foo b[ar] baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SI-5',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo b[ar] baz',
'expected': 'foo b^ar baz' }
]
},
{ 'desc': 'sel.modify: extend selection backward, creating or extending a reverse selections',
'tests': [
{ 'id': 'SM:e.b.c_TEXT-1_SC-1',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo b^ar baz',
'expected': 'foo ]b[ar baz' },
{ 'id': 'SM:e.b.c_TEXT-1_SIR-1',
'desc': 'extend selection 1 character backward',
'function': 'sel.modify("extend", "backward", "character");',
'pad': 'foo b]a[r baz',
'expected': 'foo ]ba[r baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SIR-1',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo b]a[r baz',
'expected': 'foo ]ba[r baz' },
{ 'id': 'SM:e.b.w_TEXT-1_SIR-2',
'desc': 'extend selection 1 word backward',
'function': 'sel.modify("extend", "backward", "word");',
'pad': 'foo ]ba[r baz',
'expected': ']foo ba[r baz' }
]
},
{ 'desc': 'sel.modify: extend selection forward, shrinking a reverse selections',
'tests': [
{ 'id': 'SM:e.f.c_TEXT-1_SIR-1',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo b]a[r baz',
'expected': 'foo ba^r baz' },
{ 'id': 'SM:e.f.c_TEXT-1_SIR-2',
'desc': 'extend selection 1 character forward',
'function': 'sel.modify("extend", "forward", "character");',
'pad': 'foo ]ba[r baz',
'expected': 'foo b]a[r baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SIR-1',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': 'foo ]ba[r baz',
'expected': 'foo ba^r baz' },
{ 'id': 'SM:e.f.w_TEXT-1_SIR-3',
'desc': 'extend selection 1 word forward',
'function': 'sel.modify("extend", "forward", "word");',
'pad': ']foo ba[r baz',
'expected': 'foo ]ba[r baz' }
]
},
{ 'desc': 'sel.modify: extend selection forward to line boundary',
'tests': [
{ 'id': 'SM:e.f.lb_BR.BR-1_SC-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': 'fo^o<br>bar<br>baz',
'expected': 'fo[o]<br>bar<br>baz' },
{ 'id': 'SM:e.f.lb_BR.BR-1_SI-1',
'desc': 'extend selection forward to next line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': 'fo[o]<br>bar<br>baz',
'expected': 'fo[o<br>bar]<br>baz' },
{ 'id': 'SM:e.f.lb_BR.BR-1_SM-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': 'fo[o<br>b]ar<br>baz',
'expected': 'fo[o<br>bar]<br>baz' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SC-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>fo^o</p><p>bar</p><p>baz</p>',
'expected': '<p>fo[o]</p><p>bar</p><p>baz</p>' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SI-1',
'desc': 'extend selection forward to next line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>fo[o]</p><p>bar</p><p>baz</p>',
'expected': '<p>fo[o</p><p>bar]</p><p>baz</p>' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SM-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>fo[o</p><p>b]ar</p><p>baz</p>',
'expected': '<p>fo[o</p><p>bar]</p><p>baz</p>' },
{ 'id': 'SM:e.f.lb_P.P.P-1_SMR-1',
'desc': 'extend selection forward to line boundary',
'function': 'sel.modify("extend", "forward", "lineboundary");',
'pad': '<p>foo</p><p>b]a[r</p><p>baz</p>',
'expected': '<p>foo</p><p>ba[r]</p><p>baz</p>' }
]
},
{ 'desc': 'sel.modify: extend selection backward to line boundary',
'tests': [
{ 'id': 'SM:e.b.lb_BR.BR-1_SC-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': 'foo<br>bar<br>b^az',
'expected': 'foo<br>bar<br>]b[az' },
{ 'id': 'SM:e.b.lb_BR.BR-1_SIR-2',
'desc': 'extend selection backward to previous line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': 'foo<br>bar<br>]b[az',
'expected': 'foo<br>]bar<br>b[az' },
{ 'id': 'SM:e.b.lb_BR.BR-1_SMR-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': 'foo<br>ba]r<br>b[az',
'expected': 'foo<br>]bar<br>b[az' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SC-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>bar</p><p>b^az</p>',
'expected': '<p>foo</p><p>bar</p><p>]b[az</p>' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SIR-2',
'desc': 'extend selection backward to previous line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>bar</p><p>]b[az</p>',
'expected': '<p>foo</p><p>]bar</p><p>b[az</p>' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SMR-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>ba]r</p><p>b[az</p>',
'expected': '<p>foo</p><p>]bar</p><p>b[az</p>' },
{ 'id': 'SM:e.b.lb_P.P.P-1_SM-2',
'desc': 'extend selection backward to line boundary',
'function': 'sel.modify("extend", "backward", "lineboundary");',
'pad': '<p>foo</p><p>b[a]r</p><p>baz</p>',
'expected': '<p>foo</p><p>]b[ar</p><p>baz</p>' }
]
},
{ 'desc': 'sel.modify: extend selection forward to next line (NOTE: use identical text in every line!)',
'tests': [
{ 'id': 'SM:e.f.l_BR.BR-2_SC-1',
'desc': 'extend selection forward to next line',
'function': 'sel.modify("extend", "forward", "line");',
'pad': 'fo^o<br>foo<br>foo',
'expected': 'fo[o<br>fo]o<br>foo' },
{ 'id': 'SM:e.f.l_BR.BR-2_SI-1',
'desc': 'extend selection forward to next line',
'function': 'sel.modify("extend", "forward", "line");',
'pad': 'fo[o]<br>foo<br>foo',
'expected': 'fo[o<br>foo]<br>foo' },
{ 'id': 'SM:e.f.l_BR.BR-2_SM-1',
'desc': 'extend selection forward to next line',
'function': 'sel.modify("extend", "forward", "line");',
'pad': 'fo[o<br>f]oo<br>foo',
'expected': 'fo[o<br>foo<br>f]oo' },
{ 'id': 'SM:e.f.l_P.P-1_SC-1',
'desc': 'extend selection forward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "forward", "line");',
'pad': '<p>foo^bar</p><p>foobar</p>',
'expected': '<p>foo[bar</p><p>foo]bar</p>' },
{ 'id': 'SM:e.f.l_P.P-1_SMR-1',
'desc': 'extend selection forward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "forward", "line");',
'pad': '<p>fo]obar</p><p>foob[ar</p>',
'expected': '<p>foobar</p><p>fo]ob[ar</p>' }
]
},
{ 'desc': 'sel.modify: extend selection backward to previous line (NOTE: use identical text in every line!)',
'tests': [
{ 'id': 'SM:e.b.l_BR.BR-2_SC-2',
'desc': 'extend selection backward to previous line',
'function': 'sel.modify("extend", "backward", "line");',
'pad': 'foo<br>foo<br>f^oo',
'expected': 'foo<br>f]oo<br>f[oo' },
{ 'id': 'SM:e.b.l_BR.BR-2_SIR-2',
'desc': 'extend selection backward to previous line',
'function': 'sel.modify("extend", "backward", "line");',
'pad': 'foo<br>foo<br>]f[oo',
'expected': 'foo<br>]foo<br>f[oo' },
{ 'id': 'SM:e.b.l_BR.BR-2_SMR-2',
'desc': 'extend selection backward to previous line',
'function': 'sel.modify("extend", "backward", "line");',
'pad': 'foo<br>fo]o<br>f[oo',
'expected': 'fo]o<br>foo<br>f[oo' },
{ 'id': 'SM:e.b.l_P.P-1_SC-2',
'desc': 'extend selection backward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "backward", "line");',
'pad': '<p>foobar</p><p>foo^bar</p>',
'expected': '<p>foo]bar</p><p>foo[bar</p>' },
{ 'id': 'SM:e.b.l_P.P-1_SM-1',
'desc': 'extend selection backward to next line over paragraph boundaries',
'function': 'sel.modify("extend", "backward", "line");',
'pad': '<p>fo[obar</p><p>foob]ar</p>',
'expected': '<p>fo[ob]ar</p><p>foobar</p>' }
]
},
{ 'desc': 'sel.selectAllChildren(<element>)',
'function': 'sel.selectAllChildren(doc.getElementById("div"));',
'tests': [
{ 'id': 'SAC:div_DIV-1_SC-1',
'desc': 'selectAllChildren(<div>)',
'pad': 'foo<div id="div">bar <span>ba^z</span></div>qoz',
'expected': [ 'foo<div id="div">[bar <span>baz</span>}</div>qoz',
'foo<div id="div">{bar <span>baz</span>}</div>qoz' ] },
]
}
]
}

View File

@ -0,0 +1,462 @@
UNAPPLY_TESTS = {
'id': 'U',
'caption': 'Unapply Existing Formatting Tests',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': False,
'expected': 'foo[bar]baz',
'RFC': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'remove link',
'command': 'unlink',
'tests': [
{ 'id': 'UNLINK_A-1_SO',
'desc': 'unlink wrapped <a> element',
'pad': 'foo[<a>bar</a>]baz' },
{ 'id': 'UNLINK_A-1_SW',
'desc': 'unlink <a> element where the selection wraps the full content',
'pad': 'foo<a>[bar]</a>baz' },
{ 'id': 'UNLINK_An:a.h:id-1_SO',
'desc': 'unlink wrapped <a> element that has a name and href attribute',
'pad': 'foo[<a name="A" href="#UNLINK:An:a.h:id-1_SO">bar</a>]baz' },
{ 'id': 'UNLINK_A-2_SO',
'desc': 'unlink contained <a> element',
'pad': 'foo[b<a>a</a>r]baz' },
{ 'id': 'UNLINK_A2-1_SO',
'desc': 'unlink 2 contained <a> elements',
'pad': 'foo[<a>b</a>a<a>r</a>]baz' }
]
}
],
'Proposed': [
{ 'desc': '',
'command': '',
'tests': [
]
},
{ 'desc': 'remove bold',
'command': 'bold',
'tests': [
{ 'id': 'B_B-1_SW',
'rte1-id': 'u-bold-0',
'desc': 'Selection within tags; remove <b> tags',
'pad': 'foo<b>[bar]</b>baz' },
{ 'id': 'B_B-1_SO',
'desc': 'Selection outside of tags; remove <b> tags',
'pad': 'foo[<b>bar</b>]baz' },
{ 'id': 'B_B-1_SL',
'desc': 'Selection oblique left; remove <b> tags',
'pad': 'foo[<b>bar]</b>baz' },
{ 'id': 'B_B-1_SR',
'desc': 'Selection oblique right; remove <b> tags',
'pad': 'foo<b>[bar</b>]baz' },
{ 'id': 'B_STRONG-1_SW',
'rte1-id': 'u-bold-1',
'desc': 'Selection within tags; remove <strong> tags',
'pad': 'foo<strong>[bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SO',
'desc': 'Selection outside of tags; remove <strong> tags',
'pad': 'foo[<strong>bar</strong>]baz' },
{ 'id': 'B_STRONG-1_SL',
'desc': 'Selection oblique left; remove <strong> tags',
'pad': 'foo[<strong>bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SR',
'desc': 'Selection oblique right; remove <strong> tags',
'pad': 'foo<strong>[bar</strong>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SW',
'rte1-id': 'u-bold-2',
'desc': 'Selection within tags; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SO',
'desc': 'Selection outside of tags; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar</span>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SL',
'desc': 'Selection oblique left; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SR',
'desc': 'Selection oblique right; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar</span>]baz' },
{ 'id': 'B_B-P3-1_SO12',
'desc': 'Unbolding multiple paragraphs in inside bolded content with content-model violation',
'pad': '<b>{<p>foo</p><p>bar</p>}<p>baz</p></b>',
'expected': [ '<p>[foo</p><p>bar]</p><p><b>baz</b></p>',
'<p>[foo</p><p>bar]</p><b><p>baz</p></b>' ] },
{ 'id': 'B_B-P-I..P-1_SO-I',
'desc': 'Unbolding italicized content inside bolded content with content-model violation',
'pad': '<b><p>foo[<i>bar</i>]</p><p>baz</p></b>',
'expected': [ '<p><b>foo</b><i>[bar]</i></p><p><b>baz</b></p>',
'<b><p>foo</p></b><p><i>[bar]</i></p><b><p>baz</p></b>' ] },
{ 'id': 'B_B-2_SL',
'desc': 'Remove partially covered bold, selection extends left',
'pad': 'foo [bar <b>baz] qoz</b> quz sic',
'expected': 'foo [bar baz]<b> qoz</b> quz sic' },
{ 'id': 'B_B-2_SR',
'desc': 'Remove partially covered bold, selection extends right',
'pad': 'foo bar <b>baz [qoz</b> quz] sic',
'expected': 'foo bar <b>baz </b>[qoz quz] sic' }
]
},
{ 'desc': 'remove italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SW',
'rte1-id': 'u-italic-0',
'desc': 'Selection within tags; remove <i> tags',
'pad': 'foo<i>[bar]</i>baz' },
{ 'id': 'I_I-1_SO',
'desc': 'Selection outside of tags; remove <i> tags',
'pad': 'foo[<i>bar</i>]baz' },
{ 'id': 'I_I-1_SL',
'desc': 'Selection oblique left; remove <i> tags',
'pad': 'foo[<i>bar]</i>baz' },
{ 'id': 'I_I-1_SR',
'desc': 'Selection oblique right; remove <i> tags',
'pad': 'foo<i>[bar</i>]baz' },
{ 'id': 'I_EM-1_SW',
'rte1-id': 'u-italic-1',
'desc': 'Selection within tags; remove <em> tags',
'pad': 'foo<em>[bar]</em>baz' },
{ 'id': 'I_EM-1_SO',
'desc': 'Selection outside of tags; remove <em> tags',
'pad': 'foo[<em>bar</em>]baz' },
{ 'id': 'I_EM-1_SL',
'desc': 'Selection oblique left; remove <em> tags',
'pad': 'foo[<em>bar]</em>baz' },
{ 'id': 'I_EM-1_SR',
'desc': 'Selection oblique right; remove <em> tags',
'pad': 'foo<em>[bar</em>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SW',
'rte1-id': 'u-italic-2',
'desc': 'Selection within tags; remove "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SO',
'desc': 'Selection outside of tags; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar</span>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SL',
'desc': 'Selection oblique left; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SR',
'desc': 'Selection oblique right; Italicize "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar</span>]baz' },
{ 'id': 'I_I-P3-1_SO2',
'desc': 'Unitalicize content with content-model violation',
'pad': '<i><p>foo</p>{<p>bar</p>}<p>baz</p></i>',
'expected': [ '<p><i>foo</i></p><p>[bar]</p><p><i>baz</i></p>',
'<i><p>foo</p></i><p>[bar]</p><i><p>baz</p></i>' ] }
]
},
{ 'desc': 'remove underline',
'command': 'underline',
'tests': [
{ 'id': 'U_U-1_SW',
'rte1-id': 'u-underline-0',
'desc': 'Selection within tags; remove <u> tags',
'pad': 'foo<u>[bar]</u>baz' },
{ 'id': 'U_U-1_SO',
'desc': 'Selection outside of tags; remove <u> tags',
'pad': 'foo[<u>bar</u>]baz' },
{ 'id': 'U_U-1_SL',
'desc': 'Selection oblique left; remove <u> tags',
'pad': 'foo[<u>bar]</u>baz' },
{ 'id': 'U_U-1_SR',
'desc': 'Selection oblique right; remove <u> tags',
'pad': 'foo<u>[bar</u>]baz' },
{ 'id': 'U_SPANs:td:u-1_SW',
'rte1-id': 'u-underline-1',
'desc': 'Selection within tags; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SO',
'desc': 'Selection outside of tags; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar</span>]baz' },
{ 'id': 'U_SPANs:td:u-1_SL',
'desc': 'Selection oblique left; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SR',
'desc': 'Selection oblique right; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar</span>]baz' },
{ 'id': 'U_U-S-1_SO',
'desc': 'Removing underline from underlined content with striked content',
'pad': '<u>foo[bar<s>baz</s>quoz]</u>',
'expected': '<u>foo</u>[bar<s>baz</s>quoz]' },
{ 'id': 'U_U-S-2_SI',
'desc': 'Removing underline from striked content inside underlined content',
'pad': '<u><s>foo[bar]baz</s>quoz</u>',
'expected': '<s><u>foo</u>[bar]<u>baz</u>quoz</s>' },
{ 'id': 'U_U-P3-1_SO',
'desc': 'Removing underline from underlined content with content-model violation',
'pad': '<u><p>foo</p>{<p>bar</p>}<p>baz</p></u>',
'expected': [ '<p><u>foo</u></p><p>[bar]</p><p><u>baz</u></p>',
'<u><p>foo</p></u><p>[bar]</p><u><p>baz</p></u>' ] }
]
},
{ 'desc': 'remove strike through',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_S-1_SW',
'rte1-id': 'u-strikethrough-1',
'desc': 'Selection within tags; remove <s> tags',
'pad': 'foo<s>[bar]</s>baz' },
{ 'id': 'S_S-1_SO',
'desc': 'Selection outside of tags; remove <s> tags',
'pad': 'foo[<s>bar</s>]baz' },
{ 'id': 'S_S-1_SL',
'desc': 'Selection oblique left; remove <s> tags',
'pad': 'foo[<s>bar]</s>baz' },
{ 'id': 'S_S-1_SR',
'desc': 'Selection oblique right; remove <s> tags',
'pad': 'foo<s>[bar</s>]baz' },
{ 'id': 'S_STRIKE-1_SW',
'rte1-id': 'u-strikethrough-0',
'desc': 'Selection within tags; remove <strike> tags',
'pad': 'foo<strike>[bar]</strike>baz' },
{ 'id': 'S_STRIKE-1_SO',
'desc': 'Selection outside of tags; remove <strike> tags',
'pad': 'foo[<strike>bar</strike>]baz' },
{ 'id': 'S_STRIKE-1_SL',
'desc': 'Selection oblique left; remove <strike> tags',
'pad': 'foo[<strike>bar]</strike>baz' },
{ 'id': 'S_STRIKE-2_SR',
'desc': 'Selection oblique right; remove <strike> tags',
'pad': 'foo<strike>[bar</strike>]baz' },
{ 'id': 'S_DEL-1_SW',
'rte1-id': 'u-strikethrough-2',
'desc': 'Selection within tags; remove <del> tags',
'pad': 'foo<del>[bar]</del>baz' },
{ 'id': 'S_SPANs:td:lt-1_SW',
'rte1-id': 'u-strikethrough-3',
'desc': 'Selection within tags; remove "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SO',
'desc': 'Selection outside of tags; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar</span>]baz' },
{ 'id': 'S_SPANs:td:lt-1_SL',
'desc': 'Selection oblique left; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SR',
'desc': 'Selection oblique right; Italicize "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar</span>]baz' },
{ 'id': 'S_S-U-1_SI',
'desc': 'Removing underline from underlined content inside striked content',
'pad': '<s><u>foo[bar]baz</u>quoz</s>',
'expected': '<s><u>foo</u></s><u>[bar]</u><s><u>baz</u>quoz</s>' },
{ 'id': 'S_U-S-1_SI',
'desc': 'Removing underline from striked content inside underlined content',
'pad': '<u><s>foo[bar]baz</s>quoz</u>',
'expected': '<u><s>foo</s>[bar]<s>baz</s>quoz</u>' }
]
},
{ 'desc': 'remove subscript',
'command': 'subscript',
'tests': [
{ 'id': 'SUB_SUB-1_SW',
'rte1-id': 'u-subscript-0',
'desc': 'remove subscript',
'pad': 'foo<sub>[bar]</sub>baz' },
{ 'id': 'SUB_SPANs:va:sub-1_SW',
'rte1-id': 'u-subscript-1',
'desc': 'remove subscript',
'pad': 'foo<span style="vertical-align: sub">[bar]</span>baz' }
]
},
{ 'desc': 'remove superscript',
'command': 'superscript',
'tests': [
{ 'id': 'SUP_SUP-1_SW',
'rte1-id': 'u-superscript-0',
'desc': 'remove superscript',
'pad': 'foo<sup>[bar]</sup>baz' },
{ 'id': 'SUP_SPANs:va:super-1_SW',
'rte1-id': 'u-superscript-1',
'desc': 'remove superscript',
'pad': 'foo<span style="vertical-align: super">[bar]</span>baz' }
]
},
{ 'desc': 'remove links',
'command': 'unlink',
'tests': [
{ 'id': 'UNLINK_Ahref:url-1_SW',
'rte1-id': 'u-unlink-0',
'desc': 'unlink an <a> element with href attribute where all children are selected',
'pad': 'foo<a href="http://www.goo.gl">[bar]</a>baz' },
{ 'id': 'UNLINK_A-1_SC',
'desc': 'unlink an <a> element that contains the collapsed selection',
'pad': 'foo<a>ba^r</a>baz',
'expected': 'fooba^rbaz' },
{ 'id': 'UNLINK_A-1_SI',
'desc': 'unlink an <a> element that contains the whole selection',
'pad': 'foo<a>b[a]r</a>baz',
'expected': 'foob[a]rbaz' },
{ 'id': 'UNLINK_A-2_SL',
'desc': 'unlink a partially contained <a> element',
'pad': 'foo[ba<a>r]ba</a>z' },
{ 'id': 'UNLINK_A-3_SR',
'desc': 'unlink a partially contained <a> element',
'pad': 'fo<a>o[ba</a>r]baz' },
{ 'id': 'UNLINK_As:d:b.fw:b-1_SW',
'desc': 'unlink, preserving styles',
'pad': 'foo<a href="#" style="display: block; font-weight: bold">[bar]</a>baz',
'expected': 'foo<span style="display: block; font-weight: bold">[bar]</span>baz' },
{ 'id': 'UNLINK_A-IMG-1_SO',
'desc': 'unlink a linked image at the start of the content',
'pad': '{<a href="#"><img src="pic.jpg" align="right" height="140" width="200"></a>abc]',
'expected': '{<img src="pic.jpg" align="right" height="140" width="200">abc]' }
]
},
{ 'desc': 'outdent',
'command': 'outdent',
'tests': [
{ 'id': 'OUTDENT_BQ-1_SW',
'rte1-id': 'u-outdent-0',
'desc': 'outdent (remove) a <blockquote>',
'pad': 'foo<blockquote>[bar]</blockquote>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_BQ.wibq.s:m:00040.b:n.p:0-1_SW',
'rte1-id': 'u-outdent-1',
'desc': 'outdent (remove) a styled <blockquote>',
'pad': 'foo<blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px">[bar]</blockquote>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_OL-LI-1_SW',
'rte1-id': 'u-outdent-3',
'desc': 'outdent (remove) an ordered list',
'pad': 'foo<ol><li>[bar]</li></ol>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_UL-LI-1_SW',
'rte1-id': 'u-outdent-2',
'desc': 'outdent (remove) an unordered list',
'pad': 'foo<ul><li>[bar]</li></ul>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' },
{ 'id': 'OUTDENT_DIV-1_SW',
'rte1-id': 'u-outdent-4',
'desc': 'outdent (remove) a styled <div> with margin',
'pad': 'foo<div style="margin-left: 40px;">[bar]</div>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' }
]
},
{ 'desc': 'remove all formatting',
'command': 'removeformat',
'tests': [
{ 'id': 'REMOVEFORMAT_B-1_SW',
'rte1-id': 'u-removeformat-0',
'desc': 'remove a <b> tag using "removeformat"',
'pad': 'foo<b>[bar]</b>baz' },
{ 'id': 'REMOVEFORMAT_Ahref:url-1_SW',
'rte1-id': 'u-removeformat-0',
'desc': 'remove a link using "removeformat"',
'pad': 'foo<a href="http://www.goo.gl">[bar]</a>baz' },
{ 'id': 'REMOVEFORMAT_TABLE-TBODY-TR-TD-1_SW',
'rte1-id': 'u-removeformat-2',
'desc': 'remove a table using "removeformat"',
'pad': 'foo<table><tbody><tr><td>[bar]</td></tr></tbody></table>baz',
'expected': [ 'foo<p>[bar]</p>baz',
'foo<div>[bar]</div>baz' ],
'accept': 'foo<br>[bar]<br>baz' }
]
},
{ 'desc': 'remove bookmark',
'command': 'unbookmark',
'tests': [
{ 'id': 'UNBOOKMARK_An:name-1_SW',
'rte1-id': 'u-unbookmark-0',
'desc': 'unlink a bookmark (a named <a> element) where all children are selected',
'pad': 'foo<a name="bookmark">[bar]</a>baz' }
]
}
]
}

View File

@ -0,0 +1,226 @@
UNAPPLY_TESTS_CSS = {
'id': 'UC',
'caption': 'Unapply Existing Formatting Tests, using styleWithCSS',
'checkAttrs': True,
'checkStyle': True,
'styleWithCSS': True,
'expected': 'foo[bar]baz',
'Proposed': [
{ 'desc': '',
'id': '',
'command': '',
'tests': [
]
},
{ 'desc': 'remove bold',
'command': 'bold',
'tests': [
{ 'id': 'B_B-1_SW',
'desc': 'Selection within tags; remove <b> tags',
'pad': 'foo<b>[bar]</b>baz' },
{ 'id': 'B_B-1_SO',
'desc': 'Selection outside of tags; remove <b> tags',
'pad': 'foo[<b>bar</b>]baz' },
{ 'id': 'B_B-1_SL',
'desc': 'Selection oblique left; remove <b> tags',
'pad': 'foo[<b>bar]</b>baz' },
{ 'id': 'B_B-1_SR',
'desc': 'Selection oblique right; remove <b> tags',
'pad': 'foo<b>[bar</b>]baz' },
{ 'id': 'B_STRONG-1_SW',
'desc': 'Selection within tags; remove <strong> tags',
'pad': 'foo<strong>[bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SO',
'desc': 'Selection outside of tags; remove <strong> tags',
'pad': 'foo[<strong>bar</strong>]baz' },
{ 'id': 'B_STRONG-1_SL',
'desc': 'Selection oblique left; remove <strong> tags',
'pad': 'foo[<strong>bar]</strong>baz' },
{ 'id': 'B_STRONG-1_SR',
'desc': 'Selection oblique right; remove <strong> tags',
'pad': 'foo<strong>[bar</strong>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SW',
'desc': 'Selection within tags; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SO',
'desc': 'Selection outside of tags; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar</span>]baz' },
{ 'id': 'B_SPANs:fw:b-1_SL',
'desc': 'Selection oblique left; remove "font-weight: bold"',
'pad': 'foo[<span style="font-weight: bold">bar]</span>baz' },
{ 'id': 'B_SPANs:fw:b-1_SR',
'desc': 'Selection oblique right; remove "font-weight: bold"',
'pad': 'foo<span style="font-weight: bold">[bar</span>]baz' }
]
},
{ 'desc': 'remove italic',
'command': 'italic',
'tests': [
{ 'id': 'I_I-1_SW',
'desc': 'Selection within tags; remove <i> tags',
'pad': 'foo<i>[bar]</i>baz' },
{ 'id': 'I_I-1_SO',
'desc': 'Selection outside of tags; remove <i> tags',
'pad': 'foo[<i>bar</i>]baz' },
{ 'id': 'I_I-1_SL',
'desc': 'Selection oblique left; remove <i> tags',
'pad': 'foo[<i>bar]</i>baz' },
{ 'id': 'I_I-1_SR',
'desc': 'Selection oblique right; remove <i> tags',
'pad': 'foo<i>[bar</i>]baz' },
{ 'id': 'I_EM-1_SW',
'desc': 'Selection within tags; remove <em> tags',
'pad': 'foo<em>[bar]</em>baz' },
{ 'id': 'I_EM-1_SO',
'desc': 'Selection outside of tags; remove <em> tags',
'pad': 'foo[<em>bar</em>]baz' },
{ 'id': 'I_EM-1_SL',
'desc': 'Selection oblique left; remove <em> tags',
'pad': 'foo[<em>bar]</em>baz' },
{ 'id': 'I_EM-1_SR',
'desc': 'Selection oblique right; remove <em> tags',
'pad': 'foo<em>[bar</em>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SW',
'desc': 'Selection within tags; remove "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SO',
'desc': 'Selection outside of tags; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar</span>]baz' },
{ 'id': 'I_SPANs:fs:i-1_SL',
'desc': 'Selection oblique left; Italicize "font-style: italic"',
'pad': 'foo[<span style="font-style: italic">bar]</span>baz' },
{ 'id': 'I_SPANs:fs:i-1_SR',
'desc': 'Selection oblique right; Italicize "font-style: italic"',
'pad': 'foo<span style="font-style: italic">[bar</span>]baz' }
]
},
{ 'desc': 'remove underline',
'command': 'underline',
'tests': [
{ 'id': 'U_U-1_SW',
'desc': 'Selection within tags; remove <u> tags',
'pad': 'foo<u>[bar]</u>baz' },
{ 'id': 'U_U-1_SO',
'desc': 'Selection outside of tags; remove <u> tags',
'pad': 'foo[<u>bar</u>]baz' },
{ 'id': 'U_U-1_SL',
'desc': 'Selection oblique left; remove <u> tags',
'pad': 'foo[<u>bar]</u>baz' },
{ 'id': 'U_U-1_SR',
'desc': 'Selection oblique right; remove <u> tags',
'pad': 'foo<u>[bar</u>]baz' },
{ 'id': 'U_SPANs:td:u-1_SW',
'desc': 'Selection within tags; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SO',
'desc': 'Selection outside of tags; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar</span>]baz' },
{ 'id': 'U_SPANs:td:u-1_SL',
'desc': 'Selection oblique left; remove "text-decoration: underline"',
'pad': 'foo[<span style="text-decoration: underline">bar]</span>baz' },
{ 'id': 'U_SPANs:td:u-1_SR',
'desc': 'Selection oblique right; remove "text-decoration: underline"',
'pad': 'foo<span style="text-decoration: underline">[bar</span>]baz' }
]
},
{ 'desc': 'remove strike-through',
'command': 'strikethrough',
'tests': [
{ 'id': 'S_S-1_SW',
'desc': 'Selection within tags; remove <s> tags',
'pad': 'foo<s>[bar]</s>baz' },
{ 'id': 'S_S-1_SO',
'desc': 'Selection outside of tags; remove <s> tags',
'pad': 'foo[<s>bar</s>]baz' },
{ 'id': 'S_S-1_SL',
'desc': 'Selection oblique left; remove <s> tags',
'pad': 'foo[<s>bar]</s>baz' },
{ 'id': 'S_S-1_SR',
'desc': 'Selection oblique right; remove <s> tags',
'pad': 'foo<s>[bar</s>]baz' },
{ 'id': 'S_STRIKE-1_SW',
'desc': 'Selection within tags; remove <strike> tags',
'pad': 'foo<strike>[bar]</strike>baz' },
{ 'id': 'S_STRIKE-1_SO',
'desc': 'Selection outside of tags; remove <strike> tags',
'pad': 'foo[<strike>bar</strike>]baz' },
{ 'id': 'S_STRIKE-1_SL',
'desc': 'Selection oblique left; remove <strike> tags',
'pad': 'foo[<strike>bar]</strike>baz' },
{ 'id': 'S_STRIKE-1_SR',
'desc': 'Selection oblique right; remove <strike> tags',
'pad': 'foo<strike>[bar</strike>]baz' },
{ 'id': 'S_SPANs:td:lt-1_SW',
'desc': 'Selection within tags; remove "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SO',
'desc': 'Selection outside of tags; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar</span>]baz' },
{ 'id': 'S_SPANs:td:lt-1_SL',
'desc': 'Selection oblique left; Italicize "text-decoration:line-through"',
'pad': 'foo[<span style="text-decoration:line-through">bar]</span>baz' },
{ 'id': 'S_SPANs:td:lt-1_SR',
'desc': 'Selection oblique right; Italicize "text-decoration:line-through"',
'pad': 'foo<span style="text-decoration:line-through">[bar</span>]baz' },
{ 'id': 'S_SPANc:s-1_SW',
'desc': 'Unapply "strike-through" on interited CSS style',
'checkClass': True,
'pad': 'foo<span class="s">[bar]</span>baz' },
{ 'id': 'S_SPANc:s-2_SI',
'desc': 'Unapply "strike-through" on interited CSS style',
'pad': '<span class="s">foo[bar]baz</span>',
'checkClass': True,
'expected': '<span class="s">foo</span>[bar]<span class="s">baz</span>' }
]
}
]
}

View File

@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Rich Text 2 Unit Test Example</title>
<!-- utility scripts -->
<script type="text/javascript" src="static/js/variables.js"></script>
<script type="text/javascript" src="static/js/canonicalize.js"></script>
<script type="text/javascript" src="static/js/compare.js"></script>
<script type="text/javascript" src="static/js/pad.js"></script>
<script type="text/javascript" src="static/js/range.js"></script>
<script type="text/javascript" src="static/js/units.js"></script>
<script type="text/javascript" src="static/js/run.js"></script>
<!-- you do not need static/js/output.js -->
<!--
Tests - note that those have the extensions .py,
but can be used as JS files directly.
-->
<script type="text/javascript" src="tests/selection.py"></script>
<script type="text/javascript" src="tests/apply.py"></script>
<script type="text/javascript" src="tests/applyCSS.py"></script>
<script type="text/javascript" src="tests/change.py"></script>
<script type="text/javascript" src="tests/changeCSS.py"></script>
<script type="text/javascript" src="tests/unapply.py"></script>
<script type="text/javascript" src="tests/unapplyCSS.py"></script>
<script type="text/javascript" src="tests/delete.py"></script>
<script type="text/javascript" src="tests/forwarddelete.py"></script>
<script type="text/javascript" src="tests/insert.py"></script>
<script type="text/javascript" src="tests/querySupported.py"></script>
<script type="text/javascript" src="tests/queryEnabled.py"></script>
<script type="text/javascript" src="tests/queryIndeterm.py"></script>
<script type="text/javascript" src="tests/queryState.py"></script>
<script type="text/javascript" src="tests/queryValue.py"></script>
<!-- Do something -->
<script type="text/javascript">
function runTest() {
initVariables();
initEditorDocs();
runTestSuite(UNAPPLY_TESTS);
// Below alert is just a simple demonstration on how to access the test results.
// Note that we only ran UNAPPLY tests above, so we have only results from that test set.
//
// The 'results' structure is as follows:
//
// results structure containing all results
// [<suite ID>] structure containing the results for the given suite *)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<class ID>] structure containing the results for the given class **)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<test ID>] structure containing the reults for a given test ***)
// .valscore value score (0 or 1), minimum over all containers
// .selscore selection score (0 or 1), minimum over all containers (HTML tests only)
// .valresult worst test value result (integer, see variables.js)
// .selresult worst selection result (integer, see variables.js)
// [<cont. ID>] structure containing the results of the test for a given container ****)
// .valscore value score (0 or 1)
// .selscore selection score (0 or 1)
// .valresult value result (integer, see variables.js)
// .selresult selection result (integer, see variables.js)
// .output output string (mainly for use by the online version)
// .innerHTML inner HTML of the testing container (<div> or <body>) after the test
// .outerHTML outer HTML of the testing container (<div> or <body>) after the test
// .bodyInnerHTML inner HTML of the <body> after the test
// .bodyOuterHTML outer HTML of the <body> after the test
//
// *) <suite ID>: a 1-3 character ID, e.g. UNAPPLY_TESTS.id, or 'U' (both referring the same suite)
// **) <class ID>: one of 'Proposed', 'RFC' or 'Finalized'
// ***) <test ID>: the ID of the test, without the leading 'RTE2-<suite ID>_' part
// ****) <container ID>: one of 'div' (test within a <div contenteditable="true">)
// 'dM' (test with designMode = 'on')
// 'body' (test within a <body contenteditable="true">)
alert("Result of 'Apply' tests:\nOut of " +
results[UNAPPLY_TESTS.id].count + " tests\n" +
results[UNAPPLY_TESTS.id].valscore + " had correct HTML, and\n" +
results[UNAPPLY_TESTS.id].selscore + " had a correct result selection\n(in all testing containers)." +
"\n\n" +
"Test RTE2-U_B_B-1_SW results with a contenteditable <body>:\n" +
results['U']['Proposed']['B_B-1_SW']['body'].valscore + " points for the value result, and\n" +
results['U']['Proposed']['B_B-1_SW']['body'].selscore + " points for the selection" +
""
);
}
</script>
</head>
<body onload="runTest()">
<iframe name="iframe-dM" id="iframe-dM" src="static/editable-dM.html"></iframe>
<iframe name="iframe-body" id="iframe-body" src="static/editable-body.html"></iframe>
<iframe name="iframe-div" id="iframe-div" src="static/editable-div.html"></iframe>
</body>
</html>

View File

@ -0,0 +1,19 @@
#!/bin/sh
set -x
if test -d richtext2; then
rm -drf richtext2;
fi
svn checkout http://browserscope.googlecode.com/svn/trunk/categories/richtext2 richtext2 | tail -1 | sed 's/[^0-9]//g' > current_revision
find richtext2 -type d -name .svn -exec rm -drf \{\} \; 2> /dev/null
# Remove test_set.py and other similarly named files because they confuse our mochitest runner
find richtext2 =type f -name test_\* -exec rm -rf \{\} \; 2> /dev/null
hg add current_revision richtext2
hg stat .

View File

@ -0,0 +1,263 @@
<!DOCTYPE html>
<html lang="en">
<!--
BrowserScope richtext2 category tests
This test is originally based on the unit test example available as part of the
RichText2 suite:
http://code.google.com/p/browserscope/source/browse/trunk/categories/richtext2/unittestexample.html
-->
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>BrowserScope Richtext2 Tests</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<!-- utility scripts -->
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/variables.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/canonicalize.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/compare.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/pad.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/range.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/units.js"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/static/js/run.js"></script>
<!-- you do not need static/js/output.js -->
<!--
Tests - note that those have the extensions .py,
but can be used as JS files directly.
-->
<script type="text/javascript" src="lib/richtext2/richtext2/tests/selection.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/apply.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/applyCSS.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/change.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/changeCSS.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/unapply.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/unapplyCSS.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/delete.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/forwarddelete.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/insert.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/querySupported.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryEnabled.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryIndeterm.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryState.py"></script>
<script type="text/javascript" src="lib/richtext2/richtext2/tests/queryValue.py"></script>
<script type="text/javascript" src="lib/richtext2/currentStatus.js"></script>
<!-- Do something -->
<script type="text/javascript">
// Set this constant to true in order to get the current status of the test suite.
// This is useful for updating the currentStatus.js file when an editor bug is fixed.
const UPDATE_TEST_RESULTS = false;
function runTest() {
initVariables();
initEditorDocs();
const tests = [
SELECTION_TESTS,
APPLY_TESTS,
APPLY_TESTS_CSS,
CHANGE_TESTS,
CHANGE_TESTS_CSS,
UNAPPLY_TESTS,
UNAPPLY_TESTS_CSS,
DELETE_TESTS,
FORWARDDELETE_TESTS,
INSERT_TESTS,
QUERYSUPPORTED_TESTS,
QUERYENABLED_TESTS,
QUERYINDETERM_TESTS,
QUERYSTATE_TESTS,
QUERYVALUE_TESTS,
];
for (var i = 0; i < tests.length; ++i) {
runTestSuite(tests[i]);
}
// Below alert is just a simple demonstration on how to access the test results.
// Note that we only ran UNAPPLY tests above, so we have only results from that test set.
//
// The 'results' structure is as follows:
//
// results structure containing all results
// [<suite ID>] structure containing the results for the given suite *)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<class ID>] structure containing the results for the given class **)
// .count number of tests in the given suite
// .valscore sum of all test value results (HTML or query value)
// .selscore sum of all selection results (HTML tests only)
// [<test ID>] structure containing the reults for a given test ***)
// .valscore value score (0 or 1), minimum over all containers
// .selscore selection score (0 or 1), minimum over all containers (HTML tests only)
// .valresult worst test value result (integer, see variables.js)
// .selresult worst selection result (integer, see variables.js)
// [<cont. ID>] structure containing the results of the test for a given container ****)
// .valscore value score (0 or 1)
// .selscore selection score (0 or 1)
// .valresult value result (integer, see variables.js)
// .selresult selection result (integer, see variables.js)
// .output output string (mainly for use by the online version)
// .innerHTML inner HTML of the testing container (<div> or <body>) after the test
// .outerHTML outer HTML of the testing container (<div> or <body>) after the test
// .bodyInnerHTML inner HTML of the <body> after the test
// .bodyOuterHTML outer HTML of the <body> after the test
//
// *) <suite ID>: a 1-3 character ID, e.g. UNAPPLY_TESTS.id, or 'U' (both referring the same suite)
// **) <class ID>: one of 'Proposed', 'RFC' or 'Finalized'
// ***) <test ID>: the ID of the test, without the leading 'RTE2-<suite ID>_' part
// ****) <container ID>: one of 'div' (test within a <div contenteditable="true">)
// 'dM' (test with designMode = 'on')
// 'body' (test within a <body contenteditable="true">)
if (UPDATE_TEST_RESULTS) {
var testResults = {};
for (var i = 0; i < tests.length; ++i) {
var category = tests[i];
testResults[category.id] = {};
for (var group in results[category.id]) {
switch (group) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
case "time":
break;
default:
testResults[category.id][group] = {};
for (var test_id in results[category.id][group]) {
switch (test_id) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
break;
default:
testResults[category.id][group][test_id] = {};
for (var structure in results[category.id][group][test_id]) {
switch (structure) {
// Only look at each test structure
case "dM":
case "body":
case "div":
var row = results[category.id][group][test_id][structure];
testResults[category.id][group][test_id][structure] = row;
}
}
}
}
}
}
}
var resultContainer = document.getElementById("results");
resultContainer.style.display = "";
resultContainer.textContent = JSON.stringify(testResults);
} else {
for (var i = 0; i < tests.length; ++i) {
var category = tests[i];
for (var group in results[category.id]) {
switch (group) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
case "time":
break;
default:
for (var test_id in results[category.id][group]) {
switch (test_id) {
// Skip the known properties
case "count":
case "valscore":
case "selscore":
break;
default:
for (var structure in results[category.id][group][test_id]) {
switch (structure) {
// Only look at each test structure
case "dM":
case "body":
case "div":
var row = results[category.id][group][test_id][structure];
var expected = TEST_RESULTS[category.id][group][test_id][structure];
var testName = "[" + [category.id, group, test_id, structure].join(", ") + "]";
// There are three possible cases here:
// a) If row.valscore == 1 and expected.valscore == 1, then the test has passed successfully.
// b) If row.valscore == 1 and expected.valscore == 0, a previously failing test has started to pass.
// In this case, the respective currentStatus.js entry should be modified.
// c) If row.valscore == 0 && expected.valscore == 0, then the test is known to fail.
// d) If row.valscore == 0 && expected.valscore == 1, then we've found a regression, which should be fixed.
// The same logic goes for selscore too.
if (row.valscore == 1 && expected.valscore == 1) {
// successful
is(row.valscore, expected.valscore, testName + " passed successfully");
} else if (row.valscore == 1 && expected.valscore == 0) {
// unexpected pass
todo_is(row.valscore, 1, testName + " used to fail, but it just started passing");
// debugging information
is(row.output, expected.output, "HTML test result comparison");
} else if (row.valscore == 0 && expected.valscore == 0) {
// known failure
todo_is(row.valscore, 1, testName + " is known to fail");
} else if (row.valscore == 0 && expected.valscore == 1) {
// regression
is(row.valscore, expected.valscore, testName + " has regressed");
// debugging information
is(row.output, expected.output, "HTML test result comparison");
} else {
// sanity check: this shouldn't happen!
ok(false, "Unexpected result: row = " + row.toSource() + ", expected: " + expected.toSource());
}
if (row.selscore == 1 && expected.selscore == 1) {
// successful
is(row.selscore, expected.selscore, testName + " passed successfully");
} else if (row.selscore == 1 && expected.selscore == 0) {
// unexpected pass
todo_is(row.selscore, 1, testName + " used to fail, but it just started passing");
// debugging information
is(row.output, expected.output, "HTML test result comparison");
} else if (row.selscore == 0 && expected.selscore == 0) {
// known failure
todo_is(row.selscore, 1, testName + " is known to fail");
} else if (row.selscore == 0 && expected.selscore == 1) {
// regression
is(row.selscore, expected.selscore, testName + " has regressed");
// debugging information
is(row.output, expected.output, "HTML test result comparison");
} else {
// sanity check: this shouldn't happen!
ok(false, "Unexpected result: row = " + row.toSource() + ", expected: " + expected.toSource());
}
}
}
}
}
}
}
}
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
// Running all of the tests can take a long time, try to account for it
SimpleTest.requestLongerTimeout(5);
</script>
</head>
<body onload="runTest()">
<iframe name="iframe-dM" id="iframe-dM" src="lib/richtext2/richtext2/static/editable-dM.html"></iframe>
<iframe name="iframe-body" id="iframe-body" src="lib/richtext2/richtext2/static/editable-body.html"></iframe>
<iframe name="iframe-div" id="iframe-div" src="lib/richtext2/richtext2/static/editable-div.html"></iframe>
<pre id="results" style="display: none"></pre>
</body>
</html>

View File

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=635636
-->
<head>
<title>Test for Bug 635636</title>
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635636">Mozilla Bug 635636</a>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 635636 **/
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
var w, d;
function b1()
{
w = window.open('data:application/xhtml+xml,<html xmlns="http://www.w3.org/1999/xhtml"><div>1</div></html>');
SimpleTest.waitForFocus(b2, w);
}
function b2()
{
w.document.designMode = 'on';
w.location = "data:text/plain,2";
d = w.document.getElementsByTagName("div")[0];
const Ci = Components.interfaces;
var mainWindow = w.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
var browser = mainWindow.gBrowser.selectedBrowser;
browser.addEventListener("pageshow", function() {
setTimeout(b3, 0);
}, false);
}
function b3()
{
d.parentNode.removeChild(d);
ok(true, "Should not crash");
// Not needed for the crash
w.close();
SimpleTest.finish();
}
b1();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,193 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=640321
-->
<head>
<title>Test for Bug 640321</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=640321">Mozilla Bug 640321</a>
<p id="display"></p>
<div id="content" contenteditable style="text-align: center">
<img src="green.png">
</div>
<div id="clickaway" style="width: 10px; height: 10px"></div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 640321 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var img = document.querySelector("img");
function cancel(e) { e.stopPropagation(); }
var content = document.getElementById("content");
content.addEventListener("mousedown", cancel, false);
content.addEventListener("mousemove", cancel, false);
content.addEventListener("mouseup", cancel, false);
/**
* This function is a generic resizer test.
* We have 8 resizers that we'd like to test, and each can be moved in 8 different directions.
* In specifying baseX, W can be considered to be the width of the image, and for baseY, H
* can be considered to be the height of the image. deltaX and deltaY are regular pixel values
* which can be positive or negative.
*/
const W = 1;
const H = 1;
function testResizer(baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY) {
ok(true, "testResizer(" + [baseX, baseY, deltaX, deltaY, expectedDeltaX, expectedDeltaY].join(", ") + ")");
// Reset the dimensions of the image
img.style.width = "100px";
img.style.height = "100px";
var rect = img.getBoundingClientRect();
is(rect.width, 100, "Sanity check the length");
is(rect.height, 100, "Sanity check the height");
// Click on the image to show the resizers
synthesizeMouseAtCenter(img, {});
// Determine which resizer we're dealing with
var basePosX = rect.width * baseX;
var basePosY = rect.height * baseY;
// Click on the correct resizer
synthesizeMouse(img, basePosX, basePosY, {type: "mousedown"});
// Drag it delta pixels to the right and bottom (or maybe left and top!)
synthesizeMouse(img, basePosX + deltaX, basePosY + deltaY, {type: "mousemove"});
// Release the mouse button
synthesizeMouse(img, basePosX + deltaX, basePosY + deltaY, {type: "mouseup"});
// Move the mouse delta more pixels to the same direction to make sure that the
// resize operation has stopped.
synthesizeMouse(img, basePosX + deltaX * 2, basePosY + deltaY * 2, {type: "mousemove"});
// Click outside of the image to hide the resizers
synthesizeMouseAtCenter(document.getElementById("clickaway"), {});
// Get the new dimensions for the image
var newRect = img.getBoundingClientRect();
is(newRect.width, rect.width + expectedDeltaX, "The width should be increased by " + expectedDeltaX + " pixels");
is(newRect.height, rect.height + expectedDeltaY, "The height should be increased by " + expectedDeltaY + "pixels");
}
function runTests(preserveRatio) {
// Account for changes in the resizing behavior when we're trying to preserve
// the aspect ration.
// ignoredGrowth means we don't change the size of a dimension because otherwise
// the aspect ratio would change undesirably.
// needlessGrowth means that we change the size of a dimension perpendecular to
// the mouse movement axis in order to preserve the aspect ratio.
// reversedGrowth means that we change the size of a dimension in the opposite
// direction to the mouse movement in order to maintain the aspect ratio.
const ignoredGrowth = preserveRatio ? 0 : 1;
const needlessGrowth = preserveRatio ? 1 : 0;
const reversedGrowth = preserveRatio ? -1 : 1;
SpecialPowers.setBoolPref("editor.resizing.preserve_ratio", preserveRatio);
// top resizer
testResizer(W/2, 0, -10, -10, 0, 10);
testResizer(W/2, 0, -10, 0, 0, 0);
testResizer(W/2, 0, -10, 10, 0, -10);
testResizer(W/2, 0, 0, -10, 0, 10);
testResizer(W/2, 0, 0, 0, 0, 0);
testResizer(W/2, 0, 0, 10, 0, -10);
testResizer(W/2, 0, 10, -10, 0, 10);
testResizer(W/2, 0, 10, 0, 0, 0);
testResizer(W/2, 0, 10, 10, 0, -10);
// top right resizer
testResizer( W, 0, -10, -10, -10 * reversedGrowth, 10);
testResizer( W, 0, -10, 0, -10 * ignoredGrowth, 0);
testResizer( W, 0, -10, 10, -10, -10);
testResizer( W, 0, 0, -10, 10 * needlessGrowth, 10);
testResizer( W, 0, 0, 0, 0, 0);
testResizer( W, 0, 0, 10, 0, -10 * ignoredGrowth);
testResizer( W, 0, 10, -10, 10, 10);
testResizer( W, 0, 10, 0, 10, 10 * needlessGrowth);
testResizer( W, 0, 10, 10, 10, -10 * reversedGrowth);
// right resizer
testResizer( W, H/2, -10, -10, -10, 0);
testResizer( W, H/2, -10, 0, -10, 0);
testResizer( W, H/2, -10, 10, -10, 0);
testResizer( W, H/2, 0, -10, 0, 0);
testResizer( W, H/2, 0, 0, 0, 0);
testResizer( W, H/2, 0, 10, 0, 0);
testResizer( W, H/2, 10, -10, 10, 0);
testResizer( W, H/2, 10, 0, 10, 0);
testResizer( W, H/2, 10, 10, 10, 0);
// bottom right resizer
testResizer( W, H, -10, -10, -10, -10);
testResizer( W, H, -10, 0, -10 * ignoredGrowth, 0);
testResizer( W, H, -10, 10, -10 * reversedGrowth, 10);
testResizer( W, H, 0, -10, 0, -10 * ignoredGrowth);
testResizer( W, H, 0, 0, 0, 0);
testResizer( W, H, 0, 10, 10 * needlessGrowth, 10);
testResizer( W, H, 10, -10, 10, -10 * reversedGrowth);
testResizer( W, H, 10, 0, 10, 10 * needlessGrowth);
testResizer( W, H, 10, 10, 10, 10);
// bottom resizer
testResizer(W/2, H, -10, -10, 0, -10);
testResizer(W/2, H, -10, 0, 0, 0);
testResizer(W/2, H, -10, 10, 0, 10);
testResizer(W/2, H, 0, -10, 0, -10);
testResizer(W/2, H, 0, 0, 0, 0);
testResizer(W/2, H, 0, 10, 0, 10);
testResizer(W/2, H, 10, -10, 0, -10);
testResizer(W/2, H, 10, 0, 0, 0);
testResizer(W/2, H, 10, 10, 0, 10);
// bottom left resizer
testResizer( 0, H, -10, -10, 10, -10 * reversedGrowth);
testResizer( 0, H, -10, 0, 10, 10 * needlessGrowth);
testResizer( 0, H, -10, 10, 10, 10);
testResizer( 0, H, 0, -10, 0, -10 * ignoredGrowth);
testResizer( 0, H, 0, 0, 0, 0);
testResizer( 0, H, 0, 10, 10 * needlessGrowth, 10);
testResizer( 0, H, 10, -10, -10, -10);
testResizer( 0, H, 10, 0, -10 * ignoredGrowth, 0);
testResizer( 0, H, 10, 10, -10 * reversedGrowth, 10);
// left resizer
testResizer( 0, H/2, -10, -10, 10, 0);
testResizer( 0, H/2, -10, 0, 10, 0);
testResizer( 0, H/2, -10, 10, 10, 0);
testResizer( 0, H/2, 0, -10, 0, 0);
testResizer( 0, H/2, 0, 0, 0, 0);
testResizer( 0, H/2, 0, 10, 0, 0);
testResizer( 0, H/2, 10, -10, -10, 0);
testResizer( 0, H/2, 10, 0, -10, 0);
testResizer( 0, H/2, 10, 10, -10, 0);
// top left resizer
testResizer( 0, 0, -10, -10, 10, 10);
testResizer( 0, 0, -10, 0, 10, 10 * needlessGrowth);
testResizer( 0, 0, -10, 10, 10, -10 * reversedGrowth);
testResizer( 0, 0, 0, -10, 10 * needlessGrowth, 10);
testResizer( 0, 0, 0, 0, 0, 0);
testResizer( 0, 0, 0, 10, 0, -10 * ignoredGrowth);
testResizer( 0, 0, 10, -10, -10 * reversedGrowth, 10);
testResizer( 0, 0, 10, 0, -10 * ignoredGrowth, 0);
testResizer( 0, 0, 10, 10, -10, -10);
SpecialPowers.clearUserPref("editor.resizing.preserve_ratio");
}
runTests(false);
runTests(true);
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@ -139,12 +139,14 @@ NS_INTERFACE_MAP_END_INHERITING(nsEditor)
NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags)
nsIContent *aRoot,
nsISelectionController *aSelCon,
PRUint32 aFlags)
{
NS_TIME_FUNCTION;
NS_PRECONDITION(aDoc && aPresShell, "bad arg");
NS_ENSURE_TRUE(aDoc && aPresShell, NS_ERROR_NULL_POINTER);
NS_PRECONDITION(aDoc, "bad arg");
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
nsresult res = NS_OK, rulesRes = NS_OK;
@ -154,7 +156,7 @@ NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
// Init the base editor
res = nsEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags);
res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags);
}
// check the "single line editor newline handling"
@ -671,7 +673,8 @@ nsPlaintextEditor::ExtendSelectionForDelete(nsISelection *aSelection,
|| (*aAction == ePrevious && bCollapsed)
|| *aAction == eToBeginningOfLine || *aAction == eToEndOfLine)
{
nsCOMPtr<nsISelectionController> selCont (do_QueryReferent(mSelConWeak));
nsCOMPtr<nsISelectionController> selCont;
GetSelectionController(getter_AddRefs(selCont));
NS_ENSURE_TRUE(selCont, NS_ERROR_NO_INTERFACE);
switch (*aAction)
@ -956,7 +959,8 @@ nsPlaintextEditor::UpdateIMEComposition(const nsAString& aCompositionString,
return NS_ERROR_NULL_POINTER;
}
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> ps;
GetPresShell(getter_AddRefs(ps));
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsISelection> selection;
@ -1285,7 +1289,8 @@ nsPlaintextEditor::FireClipboardEvent(PRInt32 aType)
if (aType == NS_PASTE)
ForceCompositionEnd();
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
nsCOMPtr<nsIPresShell> presShell;
GetPresShell(getter_AddRefs(presShell));
NS_ENSURE_TRUE(presShell, PR_FALSE);
nsCOMPtr<nsISelection> selection;
@ -1336,17 +1341,14 @@ nsPlaintextEditor::GetAndInitDocEncoder(const nsAString& aFormatType,
const nsACString& aCharset,
nsIDocumentEncoder** encoder)
{
nsCOMPtr<nsIPresShell> presShell;
nsresult rv = GetPresShell(getter_AddRefs(presShell));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
nsresult rv = NS_OK;
nsCAutoString formatType(NS_DOC_ENCODER_CONTRACTID_BASE);
formatType.AppendWithConversion(aFormatType);
nsCOMPtr<nsIDocumentEncoder> docEncoder (do_CreateInstance(formatType.get(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsIDocument *doc = presShell->GetDocument();
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
NS_ASSERTION(domDoc, "Need a document");

View File

@ -97,7 +97,7 @@ public:
PRBool aSuppressTransaction);
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty);
NS_IMETHOD GetIsDocumentEditable(PRBool *aIsDocumentEditable);

View File

@ -59,6 +59,8 @@ _TEST_FILES = \
test_bug604532.html \
test_bug625452.html \
test_bug629172.html \
test_bug638596.html \
test_bug641466.html \
$(NULL)
# disables the key handling test on gtk2 because gtk2 overrides some key events

View File

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=638596
-->
<head>
<title>Test for Bug 638596</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=638596">Mozilla Bug 638596</a>
<p id="display"></p>
<div id="content">
<input type="password" style="font-size: 0">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 638596 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var i = document.querySelector("input");
i.focus();
synthesizeKey("t", {});
synthesizeKey("e", {});
synthesizeKey("s", {});
synthesizeKey("t", {});
is(i.value, "test", "The correct value should be stored in the field");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=641466
-->
<head>
<title>Test for Bug 641466</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641466">Mozilla Bug 641466</a>
<p id="display"></p>
<div id="content">
<input value="&#x10451;&#x10467;&#x10455;&#x10451;">
<textarea>&#x10451;&#x10467;&#x10455;&#x10451;</textarea>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 641466 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
function doTest(element) {
element.focus();
element.selectionStart = 4;
element.selectionEnd = 4;
synthesizeKey("VK_BACKSPACE", {});
synthesizeKey("VK_BACKSPACE", {});
synthesizeKey("VK_BACKSPACE", {});
synthesizeKey("VK_BACKSPACE", {});
ok(element.value, "", "4 backspaces should delete all of the characters in the " + element.localName);
}
doTest(document.querySelector("input"));
doTest(document.querySelector("textarea"));
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@ -571,7 +571,7 @@ public class GeckoAppShell
Location loc = lm.getLastKnownLocation(provider);
if (loc != null)
sendEventToGecko(new GeckoEvent(loc));
sendEventToGecko(new GeckoEvent(loc, null));
lm.requestLocationUpdates(provider, 100, (float).5, GeckoApp.surfaceView, Looper.getMainLooper());
} else {
lm.removeUpdates(GeckoApp.surfaceView);

View File

@ -67,7 +67,6 @@ public class GeckoEvent {
public static final int ACTIVITY_PAUSING = 9;
public static final int ACTIVITY_SHUTDOWN = 10;
public static final int LOAD_URI = 11;
public static final int SURFACE_CREATED = 12;
public static final int SURFACE_DESTROYED = 13;
public static final int GECKO_EVENT_SYNC = 14;
@ -105,6 +104,7 @@ public class GeckoEvent {
public int mRangeType, mRangeStyles;
public int mRangeForeColor, mRangeBackColor;
public Location mLocation;
public Address mAddress;
public int mNativeWindow;
@ -145,9 +145,10 @@ public class GeckoEvent {
mZ = s.values[2] / SensorManager.GRAVITY_EARTH;
}
public GeckoEvent(Location l) {
public GeckoEvent(Location l, Address a) {
mType = LOCATION_EVENT;
mLocation = l;
mAddress = a;
}
public GeckoEvent(int imeAction, int offset, int count) {

View File

@ -413,10 +413,46 @@ class GeckoSurfaceView
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
}
private class GeocoderTask extends AsyncTask<Location, Void, Void> {
protected Void doInBackground(Location... location) {
try {
List<Address> addresses = mGeocoder.getFromLocation(location[0].getLatitude(),
location[0].getLongitude(), 1);
// grab the first address. in the future,
// may want to expose multiple, or filter
// for best.
mLastGeoAddress = addresses.get(0);
} catch (Exception e) {
Log.w("GeckoSurfaceView", "GeocoderTask "+e);
}
return null;
}
}
// geolocation
public void onLocationChanged(Location location)
{
GeckoAppShell.sendEventToGecko(new GeckoEvent(location));
if (mGeocoder == null)
mGeocoder = new Geocoder(getContext());
if (mLastGeoAddress == null) {
new GeocoderTask().execute(location);
}
else {
float[] results = new float[1];
Location.distanceBetween(location.getLatitude(),
location.getLongitude(),
mLastGeoAddress.getLatitude(),
mLastGeoAddress.getLongitude(),
results);
// pfm value. don't want to slam the
// geocoder with very similar values, so
// only call after about 100m
if (results[0] > 100)
new GeocoderTask().execute(location);
}
GeckoAppShell.sendEventToGecko(new GeckoEvent(location, mLastGeoAddress));
}
public void onProviderDisabled(String provider)
@ -592,6 +628,9 @@ class GeckoSurfaceView
ByteBuffer mSoftwareBuffer;
Bitmap mSoftwareBitmap;
Geocoder mGeocoder;
Address mLastGeoAddress;
final SynchronousQueue<ByteBuffer> mSyncBuf = new SynchronousQueue<ByteBuffer>();
}

View File

@ -52,7 +52,6 @@ nsFont::nsFont(const char* aName, PRUint8 aStyle, PRUint8 aVariant,
style = aStyle;
systemFont = PR_FALSE;
variant = aVariant;
familyNameQuirks = PR_FALSE;
weight = aWeight;
stretch = aStretch;
decorations = aDecoration;
@ -76,7 +75,6 @@ nsFont::nsFont(const nsString& aName, PRUint8 aStyle, PRUint8 aVariant,
style = aStyle;
systemFont = PR_FALSE;
variant = aVariant;
familyNameQuirks = PR_FALSE;
weight = aWeight;
stretch = aStretch;
decorations = aDecoration;
@ -96,7 +94,6 @@ nsFont::nsFont(const nsFont& aOther)
style = aOther.style;
systemFont = aOther.systemFont;
variant = aOther.variant;
familyNameQuirks = aOther.familyNameQuirks;
weight = aOther.weight;
stretch = aOther.stretch;
decorations = aOther.decorations;
@ -118,7 +115,6 @@ PRBool nsFont::BaseEquals(const nsFont& aOther) const
{
if ((style == aOther.style) &&
(systemFont == aOther.systemFont) &&
(familyNameQuirks == aOther.familyNameQuirks) &&
(weight == aOther.weight) &&
(stretch == aOther.stretch) &&
(size == aOther.size) &&
@ -147,7 +143,6 @@ nsFont& nsFont::operator=(const nsFont& aOther)
style = aOther.style;
systemFont = aOther.systemFont;
variant = aOther.variant;
familyNameQuirks = aOther.familyNameQuirks;
weight = aOther.weight;
stretch = aOther.stretch;
decorations = aOther.decorations;

View File

@ -78,10 +78,6 @@ struct NS_GFX nsFont {
// The variant of the font (normal, small-caps)
PRUint8 variant;
// True if the character set quirks (for treatment of "Symbol",
// "Wingdings", etc.) should be applied.
PRUint8 familyNameQuirks;
// The weight of the font; see gfxFontConstants.h.
PRUint16 weight;

View File

@ -826,7 +826,6 @@ nsThebesDeviceContext::GetSystemFont(nsSystemFontID aID, nsFont *aFont) const
aFont->style = fontStyle.style;
aFont->systemFont = fontStyle.systemFont;
aFont->variant = NS_FONT_VARIANT_NORMAL;
aFont->familyNameQuirks = fontStyle.familyNameQuirks;
aFont->weight = fontStyle.weight;
aFont->stretch = fontStyle.stretch;
aFont->decorations = NS_FONT_DECORATION_NONE;

View File

@ -82,7 +82,6 @@ nsThebesFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
mFontStyle = new gfxFontStyle(aFont.style, aFont.weight, aFont.stretch,
size, aLanguage,
aFont.sizeAdjust, aFont.systemFont,
aFont.familyNameQuirks,
printerFont,
aFont.featureSettings,
aFont.languageOverride);

View File

@ -117,19 +117,6 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
return mContext;
}
void
gfxAlphaBoxBlur::PremultiplyAlpha(gfxFloat alpha)
{
if (!mImageSurface)
return;
unsigned char* data = mImageSurface->Data();
PRInt32 length = mImageSurface->GetDataSize();
for (PRInt32 i=0; i<length; ++i)
data[i] = static_cast<unsigned char>(data[i] * alpha);
}
/**
* Box blur involves looking at one pixel, and setting its value to the average
* of its neighbouring pixels.

View File

@ -103,11 +103,6 @@ public:
return mContext;
}
/**
* Premultiplies the image by the given alpha.
*/
void PremultiplyAlpha(gfxFloat alpha);
/**
* Does the actual blurring/spreading and mask applying. Users of this
* object must have drawn whatever they want to be blurred onto the internal

View File

@ -2860,7 +2860,7 @@ gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
gfxFontStyle::gfxFontStyle() :
style(FONT_STYLE_NORMAL), systemFont(PR_TRUE), printerFont(PR_FALSE),
familyNameQuirks(PR_FALSE), weight(FONT_WEIGHT_NORMAL),
weight(FONT_WEIGHT_NORMAL),
stretch(NS_FONT_STRETCH_NORMAL), size(DEFAULT_PIXEL_FONT_SIZE),
sizeAdjust(0.0f),
language(gfxAtoms::x_western),
@ -2871,12 +2871,11 @@ gfxFontStyle::gfxFontStyle() :
gfxFontStyle::gfxFontStyle(PRUint8 aStyle, PRUint16 aWeight, PRInt16 aStretch,
gfxFloat aSize, nsIAtom *aLanguage,
float aSizeAdjust, PRPackedBool aSystemFont,
PRPackedBool aFamilyNameQuirks,
PRPackedBool aPrinterFont,
const nsString& aFeatureSettings,
const nsString& aLanguageOverride):
style(aStyle), systemFont(aSystemFont), printerFont(aPrinterFont),
familyNameQuirks(aFamilyNameQuirks), weight(aWeight), stretch(aStretch),
weight(aWeight), stretch(aStretch),
size(aSize), sizeAdjust(aSizeAdjust),
language(aLanguage),
languageOverride(ParseFontLanguageOverride(aLanguageOverride))
@ -2904,7 +2903,7 @@ gfxFontStyle::gfxFontStyle(PRUint8 aStyle, PRUint16 aWeight, PRInt16 aStretch,
gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
style(aStyle.style), systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
familyNameQuirks(aStyle.familyNameQuirks), weight(aStyle.weight),
weight(aStyle.weight),
stretch(aStyle.stretch), size(aStyle.size),
sizeAdjust(aStyle.sizeAdjust),
language(aStyle.language),

View File

@ -111,7 +111,6 @@ struct THEBES_API gfxFontStyle {
gfxFontStyle(PRUint8 aStyle, PRUint16 aWeight, PRInt16 aStretch,
gfxFloat aSize, nsIAtom *aLanguage,
float aSizeAdjust, PRPackedBool aSystemFont,
PRPackedBool aFamilyNameQuirks,
PRPackedBool aPrinterFont,
const nsString& aFeatureSettings,
const nsString& aLanguageOverride);
@ -128,10 +127,6 @@ struct THEBES_API gfxFontStyle {
// Say that this font is used for print or print preview.
PRPackedBool printerFont : 1;
// True if the character set quirks (for treatment of "Symbol",
// "Wingdings", etc.) should be applied.
PRPackedBool familyNameQuirks : 1;
// The weight of the font: 100, 200, ... 900.
PRUint16 weight;
@ -177,8 +172,8 @@ struct THEBES_API gfxFontStyle {
}
PLDHashNumber Hash() const {
return ((style + (systemFont << 7) + (familyNameQuirks << 8) +
(weight << 9)) + PRUint32(size*1000) + PRUint32(sizeAdjust*1000)) ^
return ((style + (systemFont << 7) +
(weight << 8)) + PRUint32(size*1000) + PRUint32(sizeAdjust*1000)) ^
nsISupportsHashKey::HashKey(language);
}
@ -189,7 +184,6 @@ struct THEBES_API gfxFontStyle {
(style == other.style) &&
(systemFont == other.systemFont) &&
(printerFont == other.printerFont) &&
(familyNameQuirks == other.familyNameQuirks) &&
(weight == other.weight) &&
(stretch == other.stretch) &&
(language == other.language) &&

View File

@ -2346,7 +2346,7 @@ gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern)
// FIXME: Pass a real stretch based on renderPattern!
gfxFontStyle fontStyle(style, weight, NS_FONT_STRETCH_NORMAL,
size, language, 0.0,
PR_TRUE, PR_FALSE, PR_FALSE,
PR_TRUE, PR_FALSE,
NS_LITERAL_STRING(""),
NS_LITERAL_STRING(""));

View File

@ -3434,7 +3434,7 @@ nsCSSFrameConstructor::ConstructTextFrame(const FrameConstructionData* aData,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindDataByInt(PRInt32 aInt,
nsIContent* aContent,
Element* aElement,
nsStyleContext* aStyleContext,
const FrameConstructionDataByInt* aDataPtr,
PRUint32 aDataLength)
@ -3446,7 +3446,7 @@ nsCSSFrameConstructor::FindDataByInt(PRInt32 aInt,
if (curData->mInt == aInt) {
const FrameConstructionData* data = &curData->mData;
if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
return data->mFunc.mDataGetter(aContent, aStyleContext);
return data->mFunc.mDataGetter(aElement, aStyleContext);
}
return data;
@ -3459,7 +3459,7 @@ nsCSSFrameConstructor::FindDataByInt(PRInt32 aInt,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag,
nsIContent* aContent,
Element* aElement,
nsStyleContext* aStyleContext,
const FrameConstructionDataByTag* aDataPtr,
PRUint32 aDataLength)
@ -3471,7 +3471,7 @@ nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag,
if (*curData->mTag == aTag) {
const FrameConstructionData* data = &curData->mData;
if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) {
return data->mFunc.mDataGetter(aContent, aStyleContext);
return data->mFunc.mDataGetter(aElement, aStyleContext);
}
return data;
@ -3497,7 +3497,7 @@ nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindHTMLData(nsIContent* aContent,
nsCSSFrameConstructor::FindHTMLData(Element* aElement,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsIFrame* aParentFrame,
@ -3520,9 +3520,8 @@ nsCSSFrameConstructor::FindHTMLData(nsIContent* aContent,
(aParentFrame->GetType() != nsGkAtoms::fieldSetFrame &&
aParentFrame->GetStyleContext()->GetPseudo() !=
nsCSSAnonBoxes::fieldsetContent) ||
!aContent->GetParent() ||
!aContent->GetParent()->IsHTML() ||
aContent->GetParent()->Tag() != nsGkAtoms::fieldset ||
!aElement->GetParent() ||
!aElement->GetParent()->IsHTML(nsGkAtoms::fieldset) ||
aStyleContext->GetStyleDisplay()->IsFloating() ||
aStyleContext->GetStyleDisplay()->IsAbsolutelyPositioned())) {
// <legend> is only special inside fieldset, check both the frame tree
@ -3565,16 +3564,16 @@ nsCSSFrameConstructor::FindHTMLData(nsIContent* aContent,
SIMPLE_TAG_CREATE(isindex, NS_NewIsIndexFrame)
};
return FindDataByTag(aTag, aContent, aStyleContext, sHTMLData,
return FindDataByTag(aTag, aElement, aStyleContext, sHTMLData,
NS_ARRAY_LENGTH(sHTMLData));
}
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindImgData(nsIContent* aContent,
nsCSSFrameConstructor::FindImgData(Element* aElement,
nsStyleContext* aStyleContext)
{
if (!nsImageFrame::ShouldCreateImageFrameFor(aContent, aStyleContext)) {
if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
return nsnull;
}
@ -3584,10 +3583,10 @@ nsCSSFrameConstructor::FindImgData(nsIContent* aContent,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindImgControlData(nsIContent* aContent,
nsCSSFrameConstructor::FindImgControlData(Element* aElement,
nsStyleContext* aStyleContext)
{
if (!nsImageFrame::ShouldCreateImageFrameFor(aContent, aStyleContext)) {
if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) {
return nsnull;
}
@ -3598,7 +3597,7 @@ nsCSSFrameConstructor::FindImgControlData(nsIContent* aContent,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindInputData(nsIContent* aContent,
nsCSSFrameConstructor::FindInputData(Element* aElement,
nsStyleContext* aStyleContext)
{
static const FrameConstructionDataByInt sInputData[] = {
@ -3623,28 +3622,28 @@ nsCSSFrameConstructor::FindInputData(nsIContent* aContent,
// display (in practice, none).
};
nsCOMPtr<nsIFormControl> control = do_QueryInterface(aContent);
nsCOMPtr<nsIFormControl> control = do_QueryInterface(aElement);
NS_ASSERTION(control, "input doesn't implement nsIFormControl?");
return FindDataByInt(control->GetType(), aContent, aStyleContext,
return FindDataByInt(control->GetType(), aElement, aStyleContext,
sInputData, NS_ARRAY_LENGTH(sInputData));
}
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindObjectData(nsIContent* aContent,
nsCSSFrameConstructor::FindObjectData(Element* aElement,
nsStyleContext* aStyleContext)
{
// GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for
// cases when the object is broken/suppressed/etc (e.g. a broken image), but
// we want to treat those cases as TYPE_NULL
PRUint32 type;
if (aContent->IntrinsicState().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
if (aElement->IntrinsicState().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
NS_EVENT_STATE_USERDISABLED |
NS_EVENT_STATE_SUPPRESSED)) {
type = nsIObjectLoadingContent::TYPE_NULL;
} else {
nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(aContent));
nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(aElement));
NS_ASSERTION(objContent,
"applet, embed and object must implement "
"nsIObjectLoadingContent!");
@ -3664,7 +3663,7 @@ nsCSSFrameConstructor::FindObjectData(nsIContent* aContent,
// Nothing for TYPE_NULL so we'll construct frames by display there
};
return FindDataByInt((PRInt32)type, aContent, aStyleContext,
return FindDataByInt((PRInt32)type, aElement, aStyleContext,
sObjectData, NS_ARRAY_LENGTH(sObjectData));
}
@ -4008,7 +4007,7 @@ nsIFrame* NS_NewGridBoxFrame(nsIPresShell* aPresShell,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindXULTagData(nsIContent* aContent,
nsCSSFrameConstructor::FindXULTagData(Element* aElement,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsStyleContext* aStyleContext)
@ -4056,17 +4055,17 @@ nsCSSFrameConstructor::FindXULTagData(nsIContent* aContent,
SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame)
};
return FindDataByTag(aTag, aContent, aStyleContext, sXULTagData,
return FindDataByTag(aTag, aElement, aStyleContext, sXULTagData,
NS_ARRAY_LENGTH(sXULTagData));
}
#ifdef MOZ_XUL
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindPopupGroupData(nsIContent* aContent,
nsCSSFrameConstructor::FindPopupGroupData(Element* aElement,
nsStyleContext* /* unused */)
{
if (!aContent->IsRootOfNativeAnonymousSubtree()) {
if (!aElement->IsRootOfNativeAnonymousSubtree()) {
return nsnull;
}
@ -4081,10 +4080,10 @@ nsCSSFrameConstructor::sXULTextBoxData = SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame);
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindXULLabelData(nsIContent* aContent,
nsCSSFrameConstructor::FindXULLabelData(Element* aElement,
nsStyleContext* /* unused */)
{
if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
return &sXULTextBoxData;
}
@ -4104,10 +4103,10 @@ NS_NewXULDescriptionFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindXULDescriptionData(nsIContent* aContent,
nsCSSFrameConstructor::FindXULDescriptionData(Element* aElement,
nsStyleContext* /* unused */)
{
if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
return &sXULTextBoxData;
}
@ -4119,7 +4118,7 @@ nsCSSFrameConstructor::FindXULDescriptionData(nsIContent* aContent,
#ifdef XP_MACOSX
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindXULMenubarData(nsIContent* aContent,
nsCSSFrameConstructor::FindXULMenubarData(Element* aElement,
nsStyleContext* aStyleContext)
{
nsCOMPtr<nsISupports> container =
@ -4150,7 +4149,7 @@ nsCSSFrameConstructor::FindXULMenubarData(nsIContent* aContent,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindXULListBoxBodyData(nsIContent* aContent,
nsCSSFrameConstructor::FindXULListBoxBodyData(Element* aElement,
nsStyleContext* aStyleContext)
{
if (aStyleContext->GetStyleDisplay()->mDisplay !=
@ -4165,7 +4164,7 @@ nsCSSFrameConstructor::FindXULListBoxBodyData(nsIContent* aContent,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindXULListItemData(nsIContent* aContent,
nsCSSFrameConstructor::FindXULListItemData(Element* aElement,
nsStyleContext* aStyleContext)
{
if (aStyleContext->GetStyleDisplay()->mDisplay !=
@ -4183,7 +4182,7 @@ nsCSSFrameConstructor::FindXULListItemData(nsIContent* aContent,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
nsIContent* aContent,
Element* aElement,
nsStyleContext* aStyleContext)
{
static const FrameConstructionDataByInt sXULDisplayData[] = {
@ -4207,7 +4206,7 @@ nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
};
// Processing by display here:
return FindDataByInt(aDisplay->mDisplay, aContent, aStyleContext,
return FindDataByInt(aDisplay->mDisplay, aElement, aStyleContext,
sXULDisplayData, NS_ARRAY_LENGTH(sXULDisplayData));
}
@ -4327,7 +4326,7 @@ nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState,
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
nsIContent* aContent,
Element* aElement,
nsStyleContext* aStyleContext)
{
PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)));
@ -4346,10 +4345,9 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
// XXXbz is this the right place to do this? If this code moves,
// make this function static.
PRBool propagatedScrollToViewport = PR_FALSE;
if (aContent->NodeInfo()->Equals(nsGkAtoms::body) &&
aContent->IsHTML()) {
if (aElement->IsHTML(nsGkAtoms::body)) {
propagatedScrollToViewport =
PropagateScrollToViewport() == aContent;
PropagateScrollToViewport() == aElement;
}
NS_ASSERTION(!propagatedScrollToViewport ||
@ -4369,7 +4367,7 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
// anonymous stuff.
if (mPresShell->GetPresContext()->IsPaginated() &&
aDisplay->IsBlockOutside() &&
!aContent->IsInNativeAnonymousSubtree()) {
!aElement->IsInNativeAnonymousSubtree()) {
static const FrameConstructionData sForcedNonScrollableBlockData =
FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
&nsCSSFrameConstructor::ConstructNonScrollableBlock);
@ -4444,7 +4442,7 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
&nsCSSFrameConstructor::ConstructTableCell) }
};
return FindDataByInt(aDisplay->mDisplay, aContent, aStyleContext,
return FindDataByInt(aDisplay->mDisplay, aElement, aStyleContext,
sDisplayData, NS_ARRAY_LENGTH(sDisplayData));
}
@ -4654,7 +4652,7 @@ nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindMathMLData(nsIContent* aContent,
nsCSSFrameConstructor::FindMathMLData(Element* aElement,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsStyleContext* aStyleContext)
@ -4715,7 +4713,7 @@ nsCSSFrameConstructor::FindMathMLData(nsIContent* aContent,
SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)
};
return FindDataByTag(aTag, aContent, aStyleContext, sMathMLData,
return FindDataByTag(aTag, aElement, aStyleContext, sMathMLData,
NS_ARRAY_LENGTH(sMathMLData));
}
#endif // MOZ_MATHML
@ -4731,7 +4729,7 @@ nsCSSFrameConstructor::FindMathMLData(nsIContent* aContent,
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindSVGData(nsIContent* aContent,
nsCSSFrameConstructor::FindSVGData(Element* aElement,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsIFrame* aParentFrame,
@ -4789,7 +4787,7 @@ nsCSSFrameConstructor::FindSVGData(nsIContent* aContent,
}
// We don't need frames for animation elements
if (aContent->IsNodeOfType(nsINode::eANIMATION)) {
if (aElement->IsNodeOfType(nsINode::eANIMATION)) {
return &sSuppressData;
}
@ -4807,7 +4805,7 @@ nsCSSFrameConstructor::FindSVGData(nsIContent* aContent,
return &sOuterSVGData;
}
if (!nsSVGFeatures::PassesConditionalProcessingTests(aContent)) {
if (!nsSVGFeatures::PassesConditionalProcessingTests(aElement)) {
// Elements with failing conditional processing attributes never get
// rendered. Note that this is not where we select which frame in a
// <switch> to render! That happens in nsSVGSwitchFrame::PaintSVG.
@ -4892,7 +4890,7 @@ nsCSSFrameConstructor::FindSVGData(nsIContent* aContent,
};
const FrameConstructionData* data =
FindDataByTag(aTag, aContent, aStyleContext, sSVGData,
FindDataByTag(aTag, aElement, aStyleContext, sSVGData,
NS_ARRAY_LENGTH(sSVGData));
if (!data) {
@ -5091,6 +5089,10 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState
PRUint32 aFlags,
FrameConstructionItemList& aItems)
{
NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT) ||
aContent->IsElement(),
"Shouldn't get anything else here!");
// The following code allows the user to specify the base tag
// of an element using XBL. XUL and HTML objects (like boxes, menus, etc.)
// can then be extended arbitrarily.
@ -5144,7 +5146,7 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState
return;
}
PRBool isText = aContent->IsNodeOfType(nsINode::eTEXT);
PRBool isText = !aContent->IsElement();
// never create frames for non-option/optgroup kids of <select> and
// non-option kids of <optgroup> inside a <select>.
@ -5181,46 +5183,48 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState
return;
}
} else {
Element* element = aContent->AsElement();
// Don't create frames for non-SVG element children of SVG elements.
if (aNameSpaceID != kNameSpaceID_SVG &&
aParentFrame &&
aParentFrame->IsFrameOfType(nsIFrame::eSVG) &&
!aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)
) {
SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
SetAsUndisplayedContent(aState.mFrameManager, element, styleContext,
isGeneratedContent);
return;
}
data = FindHTMLData(aContent, aTag, aNameSpaceID, aParentFrame,
data = FindHTMLData(element, aTag, aNameSpaceID, aParentFrame,
styleContext);
if (!data) {
data = FindXULTagData(aContent, aTag, aNameSpaceID, styleContext);
data = FindXULTagData(element, aTag, aNameSpaceID, styleContext);
}
#ifdef MOZ_MATHML
if (!data) {
data = FindMathMLData(aContent, aTag, aNameSpaceID, styleContext);
data = FindMathMLData(element, aTag, aNameSpaceID, styleContext);
}
#endif
if (!data) {
data = FindSVGData(aContent, aTag, aNameSpaceID, aParentFrame,
data = FindSVGData(element, aTag, aNameSpaceID, aParentFrame,
styleContext);
}
// Now check for XUL display types
if (!data) {
data = FindXULDisplayData(display, aContent, styleContext);
data = FindXULDisplayData(display, element, styleContext);
}
// And general display types
if (!data) {
data = FindDisplayData(display, aContent, styleContext);
data = FindDisplayData(display, element, styleContext);
}
NS_ASSERTION(data, "Should have frame construction data now");
if (data->mBits & FCDATA_SUPPRESS_FRAME) {
SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
SetAsUndisplayedContent(aState.mFrameManager, element, styleContext,
isGeneratedContent);
return;
}
@ -5231,7 +5235,7 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState
aParentFrame->GetType() != nsGkAtoms::menuFrame)) {
if (!aState.mPopupItems.containingBlock &&
!aState.mHavePendingPopupgroup) {
SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext,
SetAsUndisplayedContent(aState.mFrameManager, element, styleContext,
isGeneratedContent);
return;
}
@ -8879,8 +8883,7 @@ nsCSSFrameConstructor::GetInsertionPoint(nsIFrame* aParentFrame,
// have to look at insertionElement here...
if (aMultiple && !*aMultiple) {
nsIContent* content = insertionElement ? insertionElement : container;
if (content->IsHTML() &&
content->Tag() == nsGkAtoms::fieldset) {
if (content->IsHTML(nsGkAtoms::fieldset)) {
*aMultiple = PR_TRUE;
}
}
@ -10942,7 +10945,7 @@ nsCSSFrameConstructor::BuildInlineChildItems(nsFrameConstructorState& aState,
for (ChildIterator::Init(parentContent, &iter, &last);
iter != last;
++iter) {
// Manually check for comments/PIs, since we do't have a frame to pass to
// Manually check for comments/PIs, since we don't have a frame to pass to
// AddFrameConstructionItems. We know our parent is a non-replaced inline,
// so there is no need to do the NeedFrameFor check.
nsIContent* content = *iter;

View File

@ -623,7 +623,7 @@ private:
*/
struct FrameConstructionData;
typedef const FrameConstructionData*
(* FrameConstructionDataGetter)(nsIContent*, nsStyleContext*);
(* FrameConstructionDataGetter)(Element*, nsStyleContext*);
/* A constructor function that's used for complicated construction tasks.
This is expected to create the new frame, initialize it, add whatever
@ -787,7 +787,7 @@ private:
match or if the matching integer has a FrameConstructionDataGetter that
returns null. */
static const FrameConstructionData*
FindDataByInt(PRInt32 aInt, nsIContent* aContent,
FindDataByInt(PRInt32 aInt, Element* aElement,
nsStyleContext* aStyleContext,
const FrameConstructionDataByInt* aDataPtr,
PRUint32 aDataLength);
@ -798,7 +798,7 @@ private:
match or if the matching tag has a FrameConstructionDataGetter that
returns null. */
static const FrameConstructionData*
FindDataByTag(nsIAtom* aTag, nsIContent* aContent,
FindDataByTag(nsIAtom* aTag, Element* aElement,
nsStyleContext* aStyleContext,
const FrameConstructionDataByTag* aDataPtr,
PRUint32 aDataLength);
@ -1194,24 +1194,24 @@ private:
nsStyleContext* aMainStyleContext,
FrameConstructionItemList& aItems);
// Function to find FrameConstructionData for aContent. Will return
// null if aContent is not HTML.
// Function to find FrameConstructionData for aElement. Will return
// null if aElement is not HTML.
// aParentFrame might be null. If it is, that means it was an
// inline frame.
static const FrameConstructionData* FindHTMLData(nsIContent* aContent,
static const FrameConstructionData* FindHTMLData(Element* aContent,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsIFrame* aParentFrame,
nsStyleContext* aStyleContext);
// HTML data-finding helper functions
static const FrameConstructionData*
FindImgData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindImgData(Element* aElement, nsStyleContext* aStyleContext);
static const FrameConstructionData*
FindImgControlData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindImgControlData(Element* aElement, nsStyleContext* aStyleContext);
static const FrameConstructionData*
FindInputData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindInputData(Element* aElement, nsStyleContext* aStyleContext);
static const FrameConstructionData*
FindObjectData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindObjectData(Element* aElement, nsStyleContext* aStyleContext);
/* Construct a frame from the given FrameConstructionItem. This function
will handle adding the frame to frame lists, processing children, setting
@ -1289,7 +1289,7 @@ private:
// Function to find FrameConstructionData for aContent. Will return
// null if aContent is not MathML.
static const FrameConstructionData* FindMathMLData(nsIContent* aContent,
static const FrameConstructionData* FindMathMLData(Element* aElement,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsStyleContext* aStyleContext);
@ -1297,28 +1297,28 @@ private:
// Function to find FrameConstructionData for aContent. Will return
// null if aContent is not XUL.
static const FrameConstructionData* FindXULTagData(nsIContent* aContent,
static const FrameConstructionData* FindXULTagData(Element* aElement,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsStyleContext* aStyleContext);
// XUL data-finding helper functions and structures
#ifdef MOZ_XUL
static const FrameConstructionData*
FindPopupGroupData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindPopupGroupData(Element* aElement, nsStyleContext* aStyleContext);
// sXULTextBoxData used for both labels and descriptions
static const FrameConstructionData sXULTextBoxData;
static const FrameConstructionData*
FindXULLabelData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindXULLabelData(Element* aElement, nsStyleContext* aStyleContext);
static const FrameConstructionData*
FindXULDescriptionData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindXULDescriptionData(Element* aElement, nsStyleContext* aStyleContext);
#ifdef XP_MACOSX
static const FrameConstructionData*
FindXULMenubarData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindXULMenubarData(Element* aElement, nsStyleContext* aStyleContext);
#endif /* XP_MACOSX */
static const FrameConstructionData*
FindXULListBoxBodyData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindXULListBoxBodyData(Element* aElement, nsStyleContext* aStyleContext);
static const FrameConstructionData*
FindXULListItemData(nsIContent* aContent, nsStyleContext* aStyleContext);
FindXULListItemData(Element* aElement, nsStyleContext* aStyleContext);
#endif /* MOZ_XUL */
// Function to find FrameConstructionData for aContent using one of the XUL
@ -1328,12 +1328,12 @@ private:
// constructed by tag.
static const FrameConstructionData*
FindXULDisplayData(const nsStyleDisplay* aDisplay,
nsIContent* aContent,
Element* aElement,
nsStyleContext* aStyleContext);
// SVG - rods
#ifdef MOZ_SVG
static const FrameConstructionData* FindSVGData(nsIContent* aContent,
static const FrameConstructionData* FindSVGData(Element* aElement,
nsIAtom* aTag,
PRInt32 aNameSpaceID,
nsIFrame* aParentFrame,
@ -1350,7 +1350,7 @@ private:
/* Not static because it does PropagateScrollToViewport. If this
changes, make this static */
const FrameConstructionData*
FindDisplayData(const nsStyleDisplay* aDisplay, nsIContent* aContent,
FindDisplayData(const nsStyleDisplay* aDisplay, Element* aElement,
nsStyleContext* aStyleContext);
/**

View File

@ -2074,14 +2074,17 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
// multiplying by stopScale.
double stopScale;
double stopDelta = lastStop - firstStop;
if (stopDelta < 1e-6 || lineLength < 1e-6 ||
(aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR &&
(radiusX < 1e-6 || radiusY < 1e-6))) {
PRBool zeroRadius = aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR &&
(radiusX < 1e-6 || radiusY < 1e-6);
if (stopDelta < 1e-6 || lineLength < 1e-6 || zeroRadius) {
// Stops are all at the same place. Map all stops to 0.0.
// For radial gradients we need to fill with the last stop color,
// so just set both radii to 0.
// For repeating radial gradients, or for any radial gradients with
// a zero radius, we need to fill with the last stop color, so just set
// both radii to 0.
stopScale = 0.0;
radiusX = radiusY = 0.0;
if (aGradient->mRepeating || zeroRadius) {
radiusX = radiusY = 0.0;
}
lastStop = firstStop;
} else {
stopScale = 1.0/stopDelta;
@ -2115,6 +2118,11 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
// So our radii are based on radiusX.
double innerRadius = radiusX*firstStop;
double outerRadius = radiusX*lastStop;
if (stopScale == 0.0) {
// Stops are all at the same place. See above (except we now have
// the inside vs. outside of an ellipse).
outerRadius = innerRadius + 1;
}
gradientPattern = new gfxPattern(lineStart.x, lineStart.y, innerRadius,
lineStart.x, lineStart.y, outerRadius);
if (gradientPattern && radiusX != radiusY) {
@ -2135,12 +2143,11 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
// Now set normalized color stops in pattern.
if (stopScale == 0.0) {
// Non-repeating linear gradient with all stops in same place -> just add
// Non-repeating gradient with all stops in same place -> just add
// first stop and last stop, both at position 0.
// Repeating or radial gradient with all stops in the same place -> just
// paint the last stop color.
if (!aGradient->mRepeating &&
aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
// Repeating gradient with all stops in the same place, or radial
// gradient with radius of 0 -> just paint the last stop color.
if (!aGradient->mRepeating && !zeroRadius) {
gradientPattern->AddColorStop(0.0, stops[0].mColor);
}
gradientPattern->AddColorStop(0.0, stops[stops.Length() - 1].mColor);

View File

@ -60,23 +60,31 @@ using mozilla::TimeStamp;
// Compute the interval to use for the refresh driver timer, in
// milliseconds
static PRInt32
GetRefreshTimerInterval(bool aThrottled)
PRInt32
nsRefreshDriver::GetRefreshTimerInterval() const
{
const char* prefName =
aThrottled ? "layout.throttled_frame_rate" : "layout.frame_rate";
mThrottled ? "layout.throttled_frame_rate" : "layout.frame_rate";
PRInt32 rate = nsContentUtils::GetIntPref(prefName, -1);
if (rate <= 0) {
// TODO: get the rate from the platform
rate = aThrottled ? DEFAULT_THROTTLED_FRAME_RATE : DEFAULT_FRAME_RATE;
rate = mThrottled ? DEFAULT_THROTTLED_FRAME_RATE : DEFAULT_FRAME_RATE;
}
NS_ASSERTION(rate > 0, "Must have positive rate here");
return NSToIntRound(1000.0/rate);
PRInt32 interval = NSToIntRound(1000.0/rate);
if (mThrottled) {
interval = NS_MAX(interval, mLastTimerInterval * 2);
}
mLastTimerInterval = interval;
return interval;
}
static PRInt32
GetRefreshTimerType()
PRInt32
nsRefreshDriver::GetRefreshTimerType() const
{
if (mThrottled) {
return nsITimer::TYPE_ONE_SHOT;
}
PRBool precise =
nsContentUtils::GetBoolPref("layout.frame_rate.precise", PR_FALSE);
return precise ? (PRInt32)nsITimer::TYPE_REPEATING_PRECISE
@ -150,7 +158,7 @@ nsRefreshDriver::EnsureTimerStarted()
}
nsresult rv = mTimer->InitWithCallback(this,
GetRefreshTimerInterval(mThrottled),
GetRefreshTimerInterval(),
GetRefreshTimerType());
if (NS_FAILED(rv)) {
mTimer = nsnull;
@ -330,6 +338,16 @@ nsRefreshDriver::Notify(nsITimer * /* unused */)
}
}
if (mThrottled) {
// Stop the timer now and restart it here. Stopping is ok because either
// it's already one-shot, and it just fired, and all we need to do is null
// it out, or it's repeating and we need to reset it to be one-shot. Note
// that the EnsureTimerStarted() call here is ok because EnsureTimerStarted
// makes sure to not start the timer if it shouldn't be started.
StopTimer();
EnsureTimerStarted();
}
return NS_OK;
}
@ -358,10 +376,10 @@ nsRefreshDriver::SetThrottled(bool aThrottled)
if (aThrottled != mThrottled) {
mThrottled = aThrottled;
if (mTimer) {
// Stopping and restarting the timer would update our most recent refresh
// time, which isn't quite right. Luckily, we can just reschedule the
// timer.
mTimer->SetDelay(GetRefreshTimerInterval(mThrottled));
// We want to switch our timer type here, so just stop and
// restart the timer.
StopTimer();
EnsureTimerStarted();
}
}
}

View File

@ -218,6 +218,9 @@ private:
// Trigger a refresh immediately, if haven't been disconnected or frozen.
void DoRefresh();
PRInt32 GetRefreshTimerInterval() const;
PRInt32 GetRefreshTimerType() const;
nsCOMPtr<nsITimer> mTimer;
mozilla::TimeStamp mMostRecentRefresh; // only valid when mTimer non-null
PRInt64 mMostRecentRefreshEpochTime; // same thing as mMostRecentRefresh,
@ -237,6 +240,10 @@ private:
nsTArray<nsIDocument*> mBeforePaintTargets;
// nsTArray on purpose, because we want to be able to swap.
nsTArray<nsIDocument*> mAnimationFrameListenerDocs;
// This is the last interval we used for our timer. May be 0 if we
// haven't computed a timer interval yet.
mutable PRInt32 mLastTimerInterval;
};
#endif /* !defined(nsRefreshDriver_h_) */

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
var frame = document.getElementById("f");
var frameDoc = frame.contentDocument;
frameDoc.getElementById("g").style.background = "yellow";
frame.style.cssFloat = "right";
document.documentElement.offsetHeight;
frameDoc.documentElement.style.color = "green";
}
</script>
</head>
<body onload="boom();"><iframe id="f" src="data:text/html,<!DOCTYPE html><frameset><frame id=g></frameset>"></iframe></body>
</html>

View File

@ -352,3 +352,4 @@ load 603510-1.html
load 604314-1.html
load 604843.html
load 605340.html
load 621841-1.html

View File

@ -5498,6 +5498,17 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
break;
}
case eSelectWordNoSpace:
// eSelectWordNoSpace means that we should not be eating any whitespace when
// moving to the adjacent word. This means that we should set aPos->
// mWordMovementType to eEndWord if we're moving forwards, and to eStartWord
// if we're moving backwards.
if (aPos->mDirection == eDirPrevious) {
aPos->mWordMovementType = eStartWord;
} else {
aPos->mWordMovementType = eEndWord;
}
// Intentionally fall through the eSelectWord case.
case eSelectWord:
{
// wordSelectEatSpace means "are we looking for a boundary between whitespace

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