--- a/browser/installer/unix/packages-static
+++ b/browser/installer/unix/packages-static
@@ -281,16 +281,18 @@ bin/defaults/autoconfig/prefcalls.js
; Style Sheets, Graphics and other Resources used by the layout engine.
bin/res/hiddenWindow.html
bin/res/ua.css
bin/res/html.css
bin/res/quirk.css
bin/res/forms.css
bin/res/platform-forms.css
bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
bin/res/table-add-column-after-active.gif
bin/res/table-add-column-after-hover.gif
bin/res/table-add-column-after.gif
bin/res/table-add-column-before-active.gif
bin/res/table-add-column-before-hover.gif
bin/res/table-add-column-before.gif
bin/res/table-add-row-after-active.gif
bin/res/table-add-row-after-hover.gif
--- a/browser/installer/windows/packages-static
+++ b/browser/installer/windows/packages-static
@@ -268,16 +268,18 @@ bin\defaults\autoconfig\prefcalls.js
; [Layout Engine Resources]
; Style Sheets, Graphics and other Resources used by the layout engine.
bin\res\hiddenWindow.html
bin\res\ua.css
bin\res\html.css
bin\res\quirk.css
bin\res\forms.css
bin\res\EditorOverride.css
+bin\res\contenteditable.css
+bin\res\designmode.css
bin\res\table-add-column-after-active.gif
bin\res\table-add-column-after-hover.gif
bin\res\table-add-column-after.gif
bin\res\table-add-column-before-active.gif
bin\res\table-add-column-before-hover.gif
bin\res\table-add-column-before.gif
bin\res\table-add-row-after-active.gif
bin\res\table-add-row-after-hover.gif
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -58,19 +58,18 @@ class nsICSSStyleRule;
class nsRuleWalker;
class nsAttrValue;
class nsAttrName;
class nsTextFragment;
class nsIDocShell;
// IID for the nsIContent interface
#define NS_ICONTENT_IID \
-{ 0xb6408b0, 0x20c6, 0x4d60, \
- { 0xb7, 0x2f, 0x90, 0xb7, 0x7a, 0x9d, 0xb9, 0xb6 } }
-
+{ 0x36b375cb, 0xf01e, 0x4c18, \
+ { 0xbf, 0x9e, 0xba, 0xad, 0x77, 0x1d, 0xce, 0x22 } }
// hack to make egcs / gcc 2.95.2 happy
class nsIContent_base : public nsINode {
public:
#ifdef MOZILLA_INTERNAL_API
// If you're using the external API, the only thing you can know about
// nsIContent is that it exists with an IID
@@ -710,21 +709,18 @@ public:
/**
* Method to get the _intrinsic_ content state of this content node. This is
* the state that is independent of the node's presentation. To get the full
* content state, use nsIEventStateManager. Also see nsIEventStateManager
* for the possible bits that could be set here.
*/
// XXXbz this is PRInt32 because all the ESM content state APIs use
// PRInt32. We should really use PRUint32 instead.
- virtual PRInt32 IntrinsicState() const
- {
- return 0;
- }
-
+ virtual PRInt32 IntrinsicState() const;
+
/* The default script type (language) ID for this content.
All content must support fetching the default script language.
*/
virtual PRUint32 GetScriptTypeID() const
{ return nsIProgrammingLanguage::JAVASCRIPT; }
/* Not all content supports setting a new default language */
virtual nsresult SetScriptTypeID(PRUint32 aLang)
@@ -785,16 +781,22 @@ public:
/**
* Returns an atom holding the name of the "class" attribute on this
* content node (if applicable). Returns null if there is no
* "class" attribute for this type of content node.
*/
virtual nsIAtom *GetClassAttributeName() const = 0;
+ /**
+ * Should be called when the node can become editable or when it can stop
+ * being editable (for example when its contentEditable attribute changes,
+ * when it is moved into an editable parent, ...).
+ */
+ virtual void UpdateEditableState();
#ifdef DEBUG
/**
* List the content (and anything it contains) out to the given
* file stream. Use aIndent as the base indent during formatting.
*/
virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const = 0;
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -84,34 +84,36 @@ class nsNodeSupportsWeakRefTearoff;
// Forces the XBL code to treat this node as if it were
// in the document and therefore should get bindings attached.
#define NODE_FORCE_XBL_BINDINGS 0x00000040U
// Whether a binding manager may have a pointer to this
#define NODE_MAY_BE_IN_BINDING_MNGR 0x00000080U
+#define NODE_IS_EDITABLE 0x00000100U
+
// Four bits for the script-type ID
-#define NODE_SCRIPT_TYPE_OFFSET 8
+#define NODE_SCRIPT_TYPE_OFFSET 9
// Remaining bits are node type specific.
-#define NODE_TYPE_SPECIFIC_BITS_OFFSET 0x0c
+#define NODE_TYPE_SPECIFIC_BITS_OFFSET 0x0d
// Useful macro for getting a node given an nsIContent and an nsIDocument
// Returns the first argument cast to nsINode if it is non-null, otherwise
// returns the second (which may be null)
#define NODE_FROM(content_, document_) \
((content_) ? NS_STATIC_CAST(nsINode*, (content_)) : \
NS_STATIC_CAST(nsINode*, (document_)))
// IID for the nsINode interface
#define NS_INODE_IID \
-{ 0x22ab1440, 0xa6ee, 0x4da7, \
- { 0xbc, 0x3b, 0x94, 0x2e, 0x56, 0x0d, 0xdc, 0xe0 } }
+{ 0xd3e63f80, 0x9e98, 0x47d7, \
+ { 0xac, 0x8d, 0xad, 0x6f, 0x20, 0x6c, 0xe7, 0xc6 } }
// hack to make egcs / gcc 2.95.2 happy
class nsINode_base : public nsPIDOMEventTarget {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
};
/**
@@ -591,16 +593,26 @@ public:
void UnsetFlags(PtrBits aFlagsToUnset)
{
PtrBits* flags = HasSlots() ? &FlagsAsSlots()->mFlags :
&mFlagsOrSlots;
*flags &= ~aFlagsToUnset;
}
+ void SetEditableFlag(PRBool aEditable)
+ {
+ if (aEditable) {
+ SetFlags(NODE_IS_EDITABLE);
+ }
+ else {
+ UnsetFlags(NODE_IS_EDITABLE);
+ }
+ }
+
protected:
// Override this function to create a custom slots class.
virtual nsINode::nsSlots* CreateSlots();
PRBool HasSlots() const
{
return !(mFlagsOrSlots & NODE_DOESNT_HAVE_SLOTS);
--- a/content/base/public/nsISelectionPrivate.idl
+++ b/content/base/public/nsISelectionPrivate.idl
@@ -36,30 +36,31 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsISelectionListener.idl"
#include "nsIEnumerator.idl"
interface nsIDOMRange;
interface nsISelectionListener;
+interface nsIContent;
%{C++
class nsFrameSelection;
class nsIFrame;
class nsIPresShell;
struct nsPoint;
%}
[ptr] native nsFrameSelection(nsFrameSelection);
[ptr] native nsIFrame(nsIFrame);
[ptr] native nsIPresShell(nsIPresShell);
[ref] native nsPointRef(nsPoint);
-[scriptable, uuid(3225CA54-D7E1-4FF5-8EE9-091B0BFCDA1F)]
+[scriptable, uuid(b416c692-eeb8-4186-addd-c444e81b68e5)]
interface nsISelectionPrivate : nsISupports
{
const short ENDOFPRECEDINGLINE=0;
const short STARTOFNEXTLINE=1;
attribute boolean interlinePosition;
/* startBatchChanges
@@ -110,10 +111,12 @@ interface nsISelectionPrivate : nsISuppo
* Returns cached value for nsTextFrame::GetPointFromOffset.
*/
[noscript] void getCachedFrameOffset(in nsIFrame aFrame, in PRInt32 inOffset, in nsPointRef aPoint);
/* getFrameSelection
* Returnes a reference to the frame selection associated with this selection
*/
[noscript] nsFrameSelection getFrameSelection();
+
+ [noscript] void setAncestorLimiter(in nsIContent aContent);
};
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -593,16 +593,18 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
if (mText.IsBidi()) {
aDocument->SetBidiEnabled(PR_TRUE);
}
}
nsNodeUtils::ParentChainChanged(this);
+ UpdateEditableState();
+
NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");
NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
NS_POSTCONDITION(aBindingParent == GetBindingParent(),
"Bound to wrong binding parent");
return NS_OK;
}
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -295,16 +295,38 @@ nsIContent::SetNativeAnonymous(PRBool aA
SetFlags(NODE_IS_ANONYMOUS);
SetFlags(NODE_IS_ANONYMOUS_FOR_EVENTS);
} else {
UnsetFlags(NODE_IS_ANONYMOUS);
UnsetFlags(NODE_IS_ANONYMOUS_FOR_EVENTS);
}
}
+PRInt32
+nsIContent::IntrinsicState() const
+{
+ PRBool editable = HasFlag(NODE_IS_EDITABLE);
+ if (!editable) {
+ nsIDocument *doc = GetCurrentDoc();
+ if (doc) {
+ editable = doc->HasFlag(NODE_IS_EDITABLE);
+ }
+ }
+
+ return editable ? NS_EVENT_STATE_MOZ_READWRITE : NS_EVENT_STATE_MOZ_READONLY;
+}
+
+void
+nsIContent::UpdateEditableState()
+{
+ nsIContent *parent = GetParent();
+
+ SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
+}
+
//----------------------------------------------------------------------
nsChildContentList::~nsChildContentList()
{
MOZ_COUNT_DTOR(nsChildContentList);
}
NS_IMETHODIMP
@@ -1997,16 +2019,18 @@ nsGenericElement::BindToTree(nsIDocument
if (binding) {
rv = BindNodesInInsertPoints(binding, this, aDocument);
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
}
+ UpdateEditableState();
+
// Now recurse into our kids
PRUint32 i;
// Don't call GetChildCount() here since that'll make XUL generate
// template children, which we're not in a consistent enough state for.
// Additionally, there's not really a need to generate the children here.
for (i = 0; i < mAttrsAndChildren.ChildCount(); ++i) {
// The child can remove itself from the parent in BindToTree.
nsCOMPtr<nsIContent> child = mAttrsAndChildren.ChildAt(i);
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -203,16 +203,17 @@ GK_ATOM(combobox, "combobox")
GK_ATOM(command, "command")
GK_ATOM(commands, "commands")
GK_ATOM(commandset, "commandset")
GK_ATOM(commandupdate, "commandupdate")
GK_ATOM(commandupdater, "commandupdater")
GK_ATOM(comment, "comment")
GK_ATOM(compact, "compact")
GK_ATOM(concat, "concat")
+GK_ATOM(contenteditable, "contenteditable")
GK_ATOM(conditions, "conditions")
GK_ATOM(constructor, "constructor")
GK_ATOM(container, "container")
GK_ATOM(containment, "containment")
GK_ATOM(contains, "contains")
GK_ATOM(content, "content")
GK_ATOM(headerContentDisposition, "content-disposition")
GK_ATOM(headerContentLanguage, "content-language")
@@ -405,16 +406,17 @@ GK_ATOM(implements, "implements")
GK_ATOM(import, "import")
GK_ATOM(include, "include")
GK_ATOM(includes, "includes")
GK_ATOM(increment, "increment")
GK_ATOM(indent, "indent")
GK_ATOM(index, "index")
GK_ATOM(infer, "infer")
GK_ATOM(infinity, "infinity")
+GK_ATOM(inherit, "inherit")
GK_ATOM(inherits, "inherits")
GK_ATOM(inheritstyle, "inheritstyle")
GK_ATOM(input, "input")
GK_ATOM(ins, "ins")
GK_ATOM(insertafter, "insertafter")
GK_ATOM(insertbefore, "insertbefore")
GK_ATOM(instanceOf, "instanceOf")
GK_ATOM(intersection, "intersection")
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -234,17 +234,19 @@ nsTextNode::CloneDataNode(nsINodeInfo *a
#ifdef DEBUG
void
nsTextNode::List(FILE* out, PRInt32 aIndent) const
{
PRInt32 index;
for (index = aIndent; --index >= 0; ) fputs(" ", out);
- fprintf(out, "Text@%p refcount=%d<", this, mRefCnt.get());
+ fprintf(out, "Text@%p", this);
+ fprintf(out, " intrinsicstate=[%08x]", IntrinsicState());
+ fprintf(out, " refcount=%d<", mRefCnt.get());
nsAutoString tmp;
ToCString(tmp, 0, mText.GetLength());
fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
fputs(">\n", out);
}
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -1097,28 +1097,22 @@ nsGenericHTMLElement::GetSpellcheck(PRBo
}
}
// Is this a chrome element?
if (nsContentUtils::IsChromeDoc(GetOwnerDoc())) {
return NS_OK; // Not spellchecked by default
}
- // Is this the actual body of the current document?
if (IsCurrentBodyElement()) {
- // Is designMode on?
- nsCOMPtr<nsIDOMNSHTMLDocument> nsHTMLDocument =
- do_QueryInterface(GetCurrentDoc());
- if (!nsHTMLDocument) {
- return PR_FALSE;
+ nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(GetCurrentDoc());
+ if (doc) {
+ *aSpellcheck = doc->IsEditingOn();
}
- nsAutoString designMode;
- nsHTMLDocument->GetDesignMode(designMode);
- *aSpellcheck = designMode.EqualsLiteral("on");
return NS_OK;
}
// Is this element editable?
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(this);
if (!formControl) {
return NS_OK; // Not spellchecked by default
}
@@ -1159,42 +1153,77 @@ nsGenericHTMLElement::SetSpellcheck(PRBo
}
PRBool
nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc)
{
return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
}
+void
+nsGenericHTMLElement::UpdateEditableState()
+{
+ // XXX Should we do this only when in a document?
+ ContentEditableTristate value = GetContentEditableValue();
+ if (value != eInherit) {
+ SetEditableFlag(value);
+
+ return;
+ }
+
+ nsGenericElement::UpdateEditableState();
+}
+
nsresult
nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers)
{
nsresult rv = nsGenericElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
+ if (aDocument && HasFlag(NODE_IS_EDITABLE) &&
+ GetContentEditableValue() == eTrue) {
+ nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
+ if (htmlDocument) {
+ htmlDocument->ChangeContentEditableCount(this, +1);
+ }
+ }
+
// XXXbz if we already have a style attr parsed, this won't do
// anything... need to fix that.
ReparseStyleAttribute();
if (aDocument) {
// If we're in a document now, let our mapped attrs know what their new
// sheet is.
nsHTMLStyleSheet* sheet = aDocument->GetAttributeStyleSheet();
if (sheet) {
mAttrsAndChildren.SetMappedAttrStyleSheet(sheet);
}
}
return rv;
}
+void
+nsGenericHTMLElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
+{
+ if (GetContentEditableValue() == eTrue) {
+ nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetCurrentDoc());
+ if (htmlDocument) {
+ htmlDocument->ChangeContentEditableCount(this, -1);
+ }
+ }
+
+ nsGenericElement::UnbindFromTree(aDeep, aNullParent);
+}
+
already_AddRefed<nsIDOMHTMLFormElement>
nsGenericHTMLElement::FindForm(nsIForm* aCurrentForm)
{
nsIContent* content = this;
while (content) {
// If the current ancestor is a form, return it as our form
if (content->Tag() == nsGkAtoms::form &&
content->IsNodeOfType(nsINode::eHTML)) {
@@ -1394,27 +1423,59 @@ nsGenericHTMLElement::GetEventListenerMa
return rv;
}
return nsGenericElement::GetEventListenerManagerForAttr(aManager, aTarget,
aDefer);
}
nsresult
+nsGenericHTMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ nsIAtom* aPrefix, const nsAString& aValue,
+ PRBool aNotify)
+{
+ PRBool contentEditable = aNameSpaceID == kNameSpaceID_None &&
+ aName == nsGkAtoms::contenteditable;
+ PRInt32 change;
+ if (contentEditable) {
+ change = GetContentEditableValue() == eTrue ? -1 : 0;
+ }
+
+ nsresult rv = nsGenericElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
+ aNotify);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (contentEditable) {
+ if (aValue.IsEmpty() || aValue.LowerCaseEqualsLiteral("true")) {
+ change += 1;
+ }
+
+ ChangeEditableState(change);
+ }
+
+ return NS_OK;
+}
+
+nsresult
nsGenericHTMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRBool aNotify)
{
// Check for event handlers
- if (aNameSpaceID == kNameSpaceID_None &&
- nsContentUtils::IsEventAttributeName(aAttribute, EventNameType_HTML)) {
- nsCOMPtr<nsIEventListenerManager> manager;
- GetListenerManager(PR_FALSE, getter_AddRefs(manager));
-
- if (manager) {
- manager->RemoveScriptEventListener(aAttribute);
+ if (aNameSpaceID == kNameSpaceID_None) {
+ if (aAttribute == nsGkAtoms::contenteditable) {
+ ChangeEditableState(GetContentEditableValue() == eTrue ? -1 : 0);
+ }
+ else if (nsContentUtils::IsEventAttributeName(aAttribute,
+ EventNameType_HTML)) {
+ nsCOMPtr<nsIEventListenerManager> manager;
+ GetListenerManager(PR_FALSE, getter_AddRefs(manager));
+
+ if (manager) {
+ manager->RemoveScriptEventListener(aAttribute);
+ }
}
}
return nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
}
const nsAttrValue*
nsGenericHTMLElement::GetClasses() const
@@ -1575,16 +1636,21 @@ nsGenericHTMLElement::ParseAttribute(PRI
}
if (aAttribute == nsGkAtoms::name && !aValue.IsEmpty()) {
// Store name as an atom. name="" means that the element has no name,
// not that it has an emptystring as the name.
aResult.ParseAtom(aValue);
return PR_TRUE;
}
+
+ if (aAttribute == nsGkAtoms::contenteditable) {
+ aResult.ParseAtom(aValue);
+ return PR_TRUE;
+ }
}
return nsGenericElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
aResult);
}
PRBool
nsGenericHTMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
@@ -2016,29 +2082,71 @@ nsGenericHTMLElement::ParseStyleAttribut
/**
* Handle attributes common to all html elements
*/
void
nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
+ if (aData->mSID == eStyleStruct_UserInterface) {
+ nsRuleDataUserInterface *ui = aData->mUserInterfaceData;
+ if (ui->mUserModify.GetUnit() == eCSSUnit_Null) {
+ const nsAttrValue* value =
+ aAttributes->GetAttr(nsGkAtoms::contenteditable);
+ if (value) {
+ if (value->Equals(nsGkAtoms::_empty, eCaseMatters) ||
+ value->Equals(nsGkAtoms::_true, eIgnoreCase)) {
+ ui->mUserModify.SetIntValue(NS_STYLE_USER_MODIFY_READ_WRITE,
+ eCSSUnit_Enumerated);
+ }
+ else {
+ ui->mUserModify.SetIntValue(NS_STYLE_USER_MODIFY_READ_ONLY,
+ eCSSUnit_Enumerated);
+ }
+ }
+ }
+ }
if (aData->mSID == eStyleStruct_Visibility) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::lang);
if (value && value->Type() == nsAttrValue::eString) {
aData->mDisplayData->mLang.SetStringValue(value->GetStringValue(),
eCSSUnit_String);
}
}
}
+void
+nsGenericHTMLFormElement::UpdateEditableFormControlState()
+{
+ ContentEditableTristate value = GetContentEditableValue();
+ if (value != eInherit) {
+ SetEditableFlag(value);
+
+ return;
+ }
+
+ nsIContent *parent = GetParent();
+ PRBool editable = parent && parent->HasFlag(NODE_IS_EDITABLE);
+
+ if (!editable) {
+ // If not contentEditable we still need to check the readonly attribute.
+ PRBool roState;
+ GetBoolAttr(nsGkAtoms::readonly, &roState);
+
+ editable = !roState;
+ }
+
+ SetEditableFlag(editable);
+}
/* static */ const nsGenericHTMLElement::MappedAttributeEntry
nsGenericHTMLElement::sCommonAttributeMap[] = {
+ { &nsGkAtoms::contenteditable },
{ &nsGkAtoms::lang },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sImageMarginSizeAttributeMap[] = {
{ &nsGkAtoms::width },
{ &nsGkAtoms::height },
@@ -2523,16 +2631,57 @@ nsGenericHTMLElement::GetURIListAttr(nsI
if (iter >= end)
break;
}
}
return NS_OK;
}
+nsresult
+nsGenericHTMLElement::GetContentEditable(nsAString& aContentEditable)
+{
+ ContentEditableTristate value = GetContentEditableValue();
+
+ if (value == eTrue) {
+ aContentEditable.AssignLiteral("true");
+ }
+ else if (value == eFalse) {
+ aContentEditable.AssignLiteral("false");
+ }
+ else {
+ aContentEditable.AssignLiteral("inherit");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsGenericHTMLElement::SetContentEditable(const nsAString& aContentEditable)
+{
+ nsString contentEditable;
+ ToLowerCase(aContentEditable, contentEditable);
+
+ if (contentEditable.EqualsLiteral("inherit")) {
+ UnsetAttr(kNameSpaceID_None, nsGkAtoms::contenteditable, PR_TRUE);
+
+ return NS_OK;
+ }
+
+ if (!contentEditable.EqualsLiteral("true") &&
+ !contentEditable.EqualsLiteral("false")) {
+ return NS_ERROR_DOM_SYNTAX_ERR;
+ }
+
+ SetAttr(kNameSpaceID_None, nsGkAtoms::contenteditable, contentEditable,
+ PR_TRUE);
+
+ return NS_OK;
+}
+
//----------------------------------------------------------------------
NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsGenericHTMLFrameElement, TabIndex, tabindex, 0)
nsGenericHTMLFormElement::nsGenericHTMLFormElement(nsINodeInfo *aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
{
mForm = nsnull;
@@ -3148,20 +3297,29 @@ nsGenericHTMLElement::RemoveFocus(nsPres
}
PRBool
nsGenericHTMLElement::IsFocusable(PRInt32 *aTabIndex)
{
PRInt32 tabIndex = 0; // Default value for non HTML elements with -moz-user-focus
GetTabIndex(&tabIndex);
- // Just check for disabled attribute on all HTML elements
- PRBool disabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
- if (disabled) {
- tabIndex = -1;
+ PRBool disabled;
+ if (IsEditableRoot()) {
+ disabled = PR_FALSE;
+ if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
+ tabIndex = 0;
+ }
+ }
+ else {
+ // Just check for disabled attribute on all HTML elements
+ disabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
+ if (disabled) {
+ tabIndex = -1;
+ }
}
if (aTabIndex) {
*aTabIndex = tabIndex;
}
// If a tabindex is specified at all, or the default tabindex is 0, we're focusable
return tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex));
@@ -3763,8 +3921,98 @@ nsGenericHTMLElement::RecompileScriptEve
continue;
}
nsAutoString value;
GetAttr(kNameSpaceID_None, attr, value);
AddScriptEventListener(attr, value, PR_TRUE);
}
}
+
+PRBool
+nsGenericHTMLElement::IsEditableRoot() const
+{
+ nsIDocument *document = GetCurrentDoc();
+ if (!document) {
+ return PR_FALSE;
+ }
+
+ if (document->HasFlag(NODE_IS_EDITABLE)) {
+ return this == document->GetRootContent();
+ }
+
+ if (!HasFlag(NODE_IS_EDITABLE)) {
+ return PR_FALSE;
+ }
+
+ nsIContent *parent = GetParent();
+
+ return !parent || !parent->HasFlag(NODE_IS_EDITABLE);
+}
+
+nsIContent*
+nsGenericHTMLElement::FindEditableRoot()
+{
+ nsIDocument *document = GetCurrentDoc();
+ if (!document) {
+ return nsnull;
+ }
+
+ if (document->HasFlag(NODE_IS_EDITABLE)) {
+ return document->GetRootContent();
+ }
+
+ if (!HasFlag(NODE_IS_EDITABLE)) {
+ return nsnull;
+ }
+
+ nsIContent *parent, *content = this;
+ while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
+ content = parent;
+ }
+
+ return content;
+}
+
+static void
+MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
+{
+ PRInt32 stateBefore = aContent->IntrinsicState();
+
+ aContent->UpdateEditableState();
+
+ if (aDocument && stateBefore != aContent->IntrinsicState()) {
+ aDocument->ContentStatesChanged(aContent, nsnull,
+ NS_EVENT_STATE_MOZ_READONLY |
+ NS_EVENT_STATE_MOZ_READWRITE);
+ }
+
+ PRUint32 i, n = aContent->GetChildCount();
+ for (i = 0; i < n; ++i) {
+ nsIContent *child = aContent->GetChildAt(i);
+ if (!child->HasAttr(kNameSpaceID_None, nsGkAtoms::contenteditable)) {
+ MakeContentDescendantsEditable(child, aDocument);
+ }
+ }
+}
+
+void
+nsGenericHTMLElement::ChangeEditableState(PRInt32 aChange)
+{
+ nsIDocument* document = GetCurrentDoc();
+ if (!document) {
+ return;
+ }
+
+ if (aChange != 0) {
+ nsCOMPtr<nsIHTMLDocument> htmlDocument =
+ do_QueryInterface(document);
+ if (htmlDocument) {
+ htmlDocument->ChangeContentEditableCount(this, aChange);
+ }
+ }
+
+ if (document->HasFlag(NODE_IS_EDITABLE)) {
+ document = nsnull;
+ }
+
+ MakeContentDescendantsEditable(this, document);
+}
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -39,16 +39,17 @@
#define nsGenericHTMLElement_h___
#include "nsGenericElement.h"
#include "nsIDOMHTMLElement.h"
#include "nsINameSpaceManager.h" // for kNameSpaceID_None
#include "nsIFormControl.h"
#include "nsIDOMNSHTMLFrameElement.h"
#include "nsFrameLoader.h"
+#include "nsGkAtoms.h"
class nsIDOMAttr;
class nsIDOMEventListener;
class nsIDOMNodeList;
class nsIFrame;
class nsMappedAttributes;
class nsIStyleRule;
class nsISupportsArray;
@@ -160,16 +161,18 @@ public:
// SetSpellcheck() such that classes that inherit interfaces with those
// methods properly override them
NS_IMETHOD Focus();
NS_IMETHOD Blur();
NS_IMETHOD GetTabIndex(PRInt32 *aTabIndex);
NS_IMETHOD SetTabIndex(PRInt32 aTabIndex);
NS_IMETHOD GetSpellcheck(PRBool* aSpellcheck);
NS_IMETHOD SetSpellcheck(PRBool aSpellcheck);
+ nsresult GetContentEditable(nsAString &aContentEditable);
+ nsresult SetContentEditable(const nsAString &aContentEditable);
/**
* Get the frame's offset information for offsetTop/Left/Width/Height.
* @note This method flushes pending notifications (Flush_Layout).
* @param aRect the offset information [OUT]
* @param aOffsetParent the parent the offset is relative to (offsetParent)
* [OUT]
*/
@@ -191,16 +194,26 @@ public:
* @return the frame's client dimensions
*/
nsRect GetClientAreaRect();
// Implementation for nsIContent
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
+ virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
+ PRBool aNullParent = PR_TRUE);
+ nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ const nsAString& aValue, PRBool aNotify)
+ {
+ return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
+ }
+ virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ nsIAtom* aPrefix, const nsAString& aValue,
+ PRBool aNotify);
virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
PRBool aNotify);
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
virtual void RemoveFocus(nsPresContext *aPresContext);
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
virtual void PerformAccesskey(PRBool aKeyCausesActivation,
PRBool aIsTrustedEvent);
@@ -220,16 +233,18 @@ public:
// HTML element methods
void Compact() { mAttrsAndChildren.Compact(); }
const nsAttrValue* GetParsedAttr(nsIAtom* aAttr) const
{
return mAttrsAndChildren.GetAttr(aAttr);
}
virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
+ virtual void UpdateEditableState();
+
virtual const nsAttrValue* GetClasses() const;
virtual nsIAtom *GetIDAttributeName() const;
virtual nsIAtom *GetClassAttributeName() const;
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
virtual nsICSSStyleRule* GetInlineStyleRule();
NS_IMETHOD SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify);
already_AddRefed<nsIURI> GetBaseURI() const;
@@ -763,16 +778,60 @@ protected:
*/
PRBool IsCurrentBodyElement();
/**
* Ensures all editors associated with a subtree are synced, for purposes of
* spellchecking.
*/
static void SyncEditorsOnSubtree(nsIContent* content);
+
+ enum ContentEditableTristate {
+ eInherit = -1,
+ eFalse = 0,
+ eTrue = 1
+ };
+
+ /**
+ * Returns eTrue if the element has a contentEditable attribute and its value
+ * is "true" or an empty string. Returns eFalse if the element has a
+ * contentEditable attribute and its value is "false". Otherwise returns
+ * eInherit.
+ */
+ NS_HIDDEN_(ContentEditableTristate) GetContentEditableValue() const
+ {
+ static const nsIContent::AttrValuesArray values[] =
+ { &nsGkAtoms::_false, &nsGkAtoms::_true, &nsGkAtoms::_empty, nsnull };
+
+ PRInt32 value = FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::contenteditable, values,
+ eIgnoreCase);
+
+ return value > 0 ? eTrue : (value == 0 ? eFalse : eInherit);
+ }
+
+private:
+ /**
+ * Returns whether this element is an editable root. An editable root is
+ * defined as an element that is editable and whose parent is either a
+ * non-editable element or an editable document (so if the whole document is
+ * editable, then there is only one editable root, namely the
+ * documentElement).
+ */
+ PRBool IsEditableRoot() const;
+
+ /**
+ * Returns the first node amongst this node and its ancestors that is an
+ * editable root.
+ *
+ * @see IsEditableRoot for a definition of an editable root.
+ */
+ nsIContent* FindEditableRoot();
+
+ void ChangeEditableState(PRInt32 aChange);
};
//----------------------------------------------------------------------
/**
* A helper class for form elements that can contain children
*/
@@ -812,16 +871,17 @@ public:
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = PR_TRUE);
virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
PRBool aNotify);
virtual PRUint32 GetDesiredIMEState();
+ virtual PRInt32 IntrinsicState() const;
protected:
/**
* Find the form for this element and set aFormControl's form to it
* (aFormControl is passed in to avoid QI)
*
* @param aFormControl the form control to set the form for
*/
@@ -833,17 +893,17 @@ protected:
virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString* aValue, PRBool aNotify);
/**
* Returns true if the control can be disabled
*/
PRBool CanBeDisabled() const;
- virtual PRInt32 IntrinsicState() const;
+ void UpdateEditableFormControlState();
void SetFocusAndScrollIntoView(nsPresContext* aPresContext);
/** The form that contains this control */
nsIForm* mForm;
};
//----------------------------------------------------------------------
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -233,16 +233,21 @@ public:
/**
* Get the radio group container for this button (form or document)
* @return the radio group container (or null if no form or document)
*/
virtual already_AddRefed<nsIRadioGroupContainer> GetRadioGroupContainer();
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+ virtual void UpdateEditableState()
+ {
+ return UpdateEditableFormControlState();
+ }
+
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLInputElement,
nsGenericHTMLFormElement)
protected:
// Helper method
nsresult SetValueInternal(const nsAString& aValue,
nsITextControlFrame* aFrame);
@@ -596,16 +601,31 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
NS_EVENT_STATE_CHECKED |
NS_EVENT_STATE_DEFAULT |
NS_EVENT_STATE_BROKEN |
NS_EVENT_STATE_USERDISABLED |
NS_EVENT_STATE_SUPPRESSED |
NS_EVENT_STATE_LOADING);
}
}
+
+ // If readonly is changed for text and password we need to handle
+ // :read-only / :read-write
+ if (aNotify && aName == nsGkAtoms::readonly &&
+ (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD)) {
+ UpdateEditableState();
+
+ nsIDocument* document = GetCurrentDoc();
+ if (document) {
+ mozAutoDocUpdate(document, UPDATE_CONTENT_STATE, PR_TRUE);
+ document->ContentStatesChanged(this, nsnull,
+ NS_EVENT_STATE_MOZ_READONLY |
+ NS_EVENT_STATE_MOZ_READWRITE);
+ }
+ }
}
return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
aValue, aNotify);
}
// nsIDOMHTMLInputElement
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -160,16 +160,21 @@ public:
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
virtual void ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
+ virtual void UpdateEditableState()
+ {
+ return UpdateEditableFormControlState();
+ }
+
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLTextAreaElement,
nsGenericHTMLFormElement)
protected:
nsCOMPtr<nsIControllers> mControllers;
/** The current value. This is null if the frame owns the value. */
char* mValue;
/** Whether or not the value has changed since its default value was given. */
@@ -197,16 +202,19 @@ protected:
nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
/**
* Common method to call from the various mutation observer methods.
* aContent is a content node that's either the one that changed or its
* parent; we should only respond to the change if aContent is non-anonymous.
*/
void ContentChanged(nsIContent* aContent);
+
+ virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
+ const nsAString* aValue, PRBool aNotify);
};
NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
nsHTMLTextAreaElement::nsHTMLTextAreaElement(nsINodeInfo *aNodeInfo,
PRBool aFromParser)
@@ -985,8 +993,28 @@ nsHTMLTextAreaElement::ContentRemoved(ns
void
nsHTMLTextAreaElement::ContentChanged(nsIContent* aContent)
{
if (!mValueChanged && mDoneAddingChildren &&
nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
Reset();
}
}
+
+nsresult
+nsHTMLTextAreaElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ const nsAString* aValue, PRBool aNotify)
+{
+ if (aNotify && aNameSpaceID == kNameSpaceID_None &&
+ aName == nsGkAtoms::readonly) {
+ UpdateEditableState();
+
+ nsIDocument* document = GetCurrentDoc();
+ if (document) {
+ mozAutoDocUpdate(document, UPDATE_CONTENT_STATE, PR_TRUE);
+ document->ContentStatesChanged(this, nsnull,
+ NS_EVENT_STATE_MOZ_READONLY |
+ NS_EVENT_STATE_MOZ_READWRITE);
+ }
+ }
+ return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
+ aNotify);
+}
--- a/content/html/document/src/Makefile.in
+++ b/content/html/document/src/Makefile.in
@@ -71,16 +71,17 @@ REQUIRES = xpcom \
lwbrk \
imglib2 \
xpconnect \
unicharutil \
commandhandler \
composer \
editor \
plugin \
+ txtsvc \
$(NULL)
CPPSRCS = \
nsHTMLContentSink.cpp \
nsHTMLFragmentContentSink.cpp \
nsHTMLDocument.cpp \
nsMediaDocument.cpp \
nsPluginDocument.cpp \
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -120,24 +120,30 @@
#include "nsIJSContextStack.h"
#include "nsIDocumentViewer.h"
#include "nsIWyciwygChannel.h"
#include "nsIScriptElement.h"
#include "nsIScriptError.h"
#include "nsIMutableArray.h"
#include "nsArrayUtils.h"
#include "nsIEffectiveTLDService.h"
+#include "nsIEventStateManager.h"
#include "nsIPrompt.h"
//AHMED 12-2
#include "nsBidiUtils.h"
#include "nsIEditingSession.h"
#include "nsIEditor.h"
#include "nsNodeInfoManager.h"
+#include "nsIEditor.h"
+#include "nsIEditorDocShell.h"
+#include "nsIEditorStyleSheets.h"
+#include "nsIInlineSpellChecker.h"
+#include "nsRange.h"
#define NS_MAX_DOCUMENT_WRITE_DEPTH 20
#define DETECTOR_CONTRACTID_MAX 127
static char g_detector_contractid[DETECTOR_CONTRACTID_MAX + 1];
static PRBool gInitDetector = PR_FALSE;
static PRBool gPlugDetector = PR_FALSE;
@@ -1199,18 +1205,23 @@ nsHTMLDocument::EndLoad()
}
// Reset this now, since we're really done "loading" this document.written
// document.
NS_ASSERTION(mWriteState == eNotWriting || mWriteState == ePendingClose ||
mWriteState == eDocumentClosed, "EndLoad called early");
mWriteState = eNotWriting;
+ PRBool turnOnEditing =
+ mParser && (HasFlag(NODE_IS_EDITABLE) || mContentEditableCount > 0);
// Note: nsDocument::EndLoad nulls out mParser.
nsDocument::EndLoad();
+ if (turnOnEditing) {
+ EditingStateChanged();
+ }
}
NS_IMETHODIMP
nsHTMLDocument::SetTitle(const nsAString& aTitle)
{
return nsDocument::SetTitle(aTitle);
}
@@ -2202,24 +2213,24 @@ nsHTMLDocument::OpenCommon(const nsACStr
// for us and that'll create frames for the root element and the
// scrollbars work as expected (since the document in the root
// element was never set to null)
mChildren.AppendChild(root);
mRootContent = root;
}
- if (mEditingIsOn) {
+ if (IsEditingOn()) {
// Reset() blows away all event listeners in the document, and our
// editor relies heavily on those. Midas is turned on, to make it
// work, re-initialize it to give it a chance to add its event
// listeners again.
- SetDesignMode(NS_LITERAL_STRING("off"));
- SetDesignMode(NS_LITERAL_STRING("on"));
+ TurnEditingOff();
+ EditingStateChanged();
}
// Zap the old title -- otherwise it would hang around until document.close()
// (which might never come) if the new document doesn't explicitly set one.
// Void the title to make sure that we actually respect any titles set by the
// new document.
SetTitle(EmptyString());
mDocumentTitle.SetIsVoid(PR_TRUE);
@@ -3712,97 +3723,311 @@ nsHTMLDocument::GenerateParserKey(void)
// which is guaranteed to be unique per script.
return mScriptLoader->GetCurrentScript();
}
/* attribute DOMString designMode; */
NS_IMETHODIMP
nsHTMLDocument::GetDesignMode(nsAString & aDesignMode)
{
- if (mEditingIsOn) {
+ if (HasFlag(NODE_IS_EDITABLE)) {
aDesignMode.AssignLiteral("on");
}
else {
aDesignMode.AssignLiteral("off");
}
return NS_OK;
}
-NS_IMETHODIMP
-nsHTMLDocument::SetDesignMode(const nsAString & aDesignMode)
+nsresult
+nsHTMLDocument::ChangeContentEditableCount(nsIContent *aElement,
+ PRInt32 aChange)
+{
+ NS_ASSERTION(mContentEditableCount + aChange >= 0,
+ "Trying to decrement too much.");
+
+ mContentEditableCount += aChange;
+
+ if (mParser) {
+ return NS_OK;
+ }
+
+ EditingState oldState = mEditingState;
+
+ nsresult rv = EditingStateChanged();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (oldState == mEditingState && mEditingState == eContentEditable) {
+ // We just changed the contentEditable state of a node, we need to reset
+ // the spellchecking state of that node.
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
+ if (node) {
+ nsPIDOMWindow *window = GetWindow();
+ if (!window)
+ return NS_ERROR_FAILURE;
+
+ nsIDocShell *docshell = window->GetDocShell();
+ if (!docshell)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIEditorDocShell> editorDocShell =
+ do_QueryInterface(docshell, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIEditor> editor;
+ rv = editorDocShell->GetEditor(getter_AddRefs(editor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMRange> range;
+ rv = NS_NewRange(getter_AddRefs(range));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = range->SelectNode(node);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInlineSpellChecker> spellChecker;
+ rv = editor->GetInlineSpellChecker(PR_FALSE,
+ getter_AddRefs(spellChecker));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (spellChecker) {
+ rv = spellChecker->SpellCheckRange(range);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+static void
+NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument,
+ PRBool aEditable)
{
+ PRUint32 i, n = aNode->GetChildCount();
+ for (i = 0; i < n; ++i) {
+ nsIContent *child = aNode->GetChildAt(i);
+ if (child->HasFlag(NODE_IS_EDITABLE) != aEditable) {
+ aDocument->ContentStatesChanged(child, nsnull,
+ NS_EVENT_STATE_MOZ_READONLY |
+ NS_EVENT_STATE_MOZ_READWRITE);
+ }
+ NotifyEditableStateChange(child, aDocument, aEditable);
+ }
+}
+
+nsresult
+nsHTMLDocument::TurnEditingOff()
+{
+ NS_ASSERTION(mEditingState != eOff, "Editing is already off.");
+
+ nsPIDOMWindow *window = GetWindow();
+ if (!window)
+ return NS_ERROR_FAILURE;
+
+ nsIDocShell *docshell = window->GetDocShell();
+ if (!docshell)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+ nsCOMPtr<nsIEditorDocShell> editorDocShell =
+ do_QueryInterface(docshell, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIEditor> editor;
+ rv = editorDocShell->GetEditor(getter_AddRefs(editor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // turn editing off
+ rv = editSession->TearDownEditorOnWindow(window, PR_TRUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIEditorStyleSheets> editorss = do_QueryInterface(editor, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!HasFlag(NODE_IS_EDITABLE)) {
+ editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/contenteditable.css"));
+ editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/designmode.css"));
+
+ rv = docshell->SetAllowJavascript(mScriptsEnabled);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = docshell->SetAllowPlugins(mPluginsEnabled);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ mEditingState = eOff;
+
+ return NS_OK;
+}
+
+nsresult
+nsHTMLDocument::EditingStateChanged()
+{
+ PRBool designMode = HasFlag(NODE_IS_EDITABLE);
+ EditingState newState = designMode ? eDesignMode :
+ (mContentEditableCount > 0 ? eContentEditable : eOff);
+ if (mEditingState == newState) {
+ // No changes in editing mode.
+ return NS_OK;
+ }
+
+ if (newState == eOff) {
+ // Editing is being turned off.
+ return TurnEditingOff();
+ }
+
// get editing session
nsPIDOMWindow *window = GetWindow();
if (!window)
return NS_ERROR_FAILURE;
nsIDocShell *docshell = window->GetDocShell();
if (!docshell)
return NS_ERROR_FAILURE;
+ nsresult rv;
+ nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRBool makeWindowEditable = (mEditingState == eOff);
+ if (makeWindowEditable) {
+ // Editing is being turned on (through designMode or contentEditable)
+ // Turn on editor.
+ rv = editSession->MakeWindowEditable(window, "html", PR_FALSE, PR_FALSE,
+ PR_TRUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // XXX Need to call TearDownEditorOnWindow for all failures.
+ nsCOMPtr<nsIEditorDocShell> editorDocShell =
+ do_QueryInterface(docshell, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIEditor> editor;
+ rv = editorDocShell->GetEditor(getter_AddRefs(editor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIEditorStyleSheets> editorss = do_QueryInterface(editor, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ editorss->AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/contenteditable.css"));
+
+ // Should we update the editable state of all the nodes in the document? We
+ // need to do this when the designMode value changes, as that overrides
+ // specific states on the elements.
+ PRBool updateState;
+
+ PRBool spellRecheckAll = PR_FALSE;
+ if (designMode) {
+ // designMode is being turned on (overrides contentEditable).
+ editorss->AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/designmode.css"));
+
+ // Store scripting and plugins state.
+ PRBool tmp;
+ rv = docshell->GetAllowJavascript(&tmp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mScriptsEnabled = tmp;
+
+ rv = docshell->GetAllowPlugins(&tmp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mPluginsEnabled = tmp;
+
+ updateState = PR_TRUE;
+ spellRecheckAll = mEditingState == eContentEditable;
+ }
+ else if (mEditingState == eDesignMode) {
+ // designMode is being turned off (contentEditable is still on).
+ editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/designmode.css"));
+
+ rv = docshell->SetAllowJavascript(mScriptsEnabled);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = docshell->SetAllowPlugins(mPluginsEnabled);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ updateState = PR_TRUE;
+ }
+ else {
+ // contentEditable is being turned on (and designMode is off).
+ updateState = PR_FALSE;
+ }
+
+ mEditingState = newState;
+
+ if (makeWindowEditable) {
+ // Set the editor to not insert br's on return when in p
+ // elements by default.
+ // XXX Do we only want to do this for designMode?
+ PRBool unused;
+ rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), PR_FALSE,
+ NS_LITERAL_STRING("false"), &unused);
+
+ if (NS_FAILED(rv)) {
+ // Editor setup failed. Editing is not on after all.
+ // XXX Should we reset the editable flag on nodes?
+ editSession->TearDownEditorOnWindow(window, PR_TRUE);
+ mEditingState = eOff;
+
+ return rv;
+ }
+ }
+
+ if (updateState) {
+ mozAutoDocUpdate(this, UPDATE_CONTENT_STATE, PR_TRUE);
+ NotifyEditableStateChange(this, this, !designMode);
+ }
+
+ // Resync the editor's spellcheck state.
+ if (spellRecheckAll) {
+ nsCOMPtr<nsISelectionController> selcon;
+ nsresult rv = editor->GetSelectionController(getter_AddRefs(selcon));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISelection> spellCheckSelection;
+ rv = selcon->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
+ getter_AddRefs(spellCheckSelection));
+ if (NS_SUCCEEDED(rv)) {
+ spellCheckSelection->RemoveAllRanges();
+ }
+ }
+ editor->SyncRealTimeSpell();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLDocument::SetDesignMode(const nsAString & aDesignMode)
+{
nsresult rv = NS_OK;
if (!nsContentUtils::IsCallerTrustedForWrite()) {
nsCOMPtr<nsIPrincipal> subject;
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
NS_ENSURE_SUCCESS(rv, rv);
if (subject) {
rv = secMan->CheckSameOriginPrincipal(subject, NodePrincipal());
NS_ENSURE_SUCCESS(rv, rv);
}
}
- nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell);
- if (!editSession)
- return NS_ERROR_FAILURE;
-
- if (aDesignMode.LowerCaseEqualsLiteral("on") && !mEditingIsOn) {
- rv = editSession->MakeWindowEditable(window, "html", PR_FALSE);
-
- if (NS_SUCCEEDED(rv)) {
- // now that we've successfully created the editor, we can
- // reset our flag
- mEditingIsOn = PR_TRUE;
-
- // Set the editor to not insert br's on return when in p
- // elements by default.
- PRBool unused;
- rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), PR_FALSE,
- NS_LITERAL_STRING("false"), &unused);
-
- if (NS_FAILED(rv)) {
- // Editor setup failed. Editing is is not on after all.
-
- editSession->TearDownEditorOnWindow(window);
-
- mEditingIsOn = PR_FALSE;
- } else {
- // Resync the editor's spellcheck state, since when the editor was
- // created it asked us whether designMode was on, and we told it no.
- // Note that reporting "yes" (by setting mEditingIsOn true before
- // calling MakeWindowEditable()) exposed several crash bugs (see bugs
- // 348497, 348981).
- nsCOMPtr<nsIEditor> editor;
- rv = editSession->GetEditorForWindow(window, getter_AddRefs(editor));
- if (NS_SUCCEEDED(rv)) {
- editor->SyncRealTimeSpell();
- }
- }
- }
- } else if (aDesignMode.LowerCaseEqualsLiteral("off") && mEditingIsOn) {
- // turn editing off
- rv = editSession->TearDownEditorOnWindow(window);
-
- if (NS_SUCCEEDED(rv)) {
- mEditingIsOn = PR_FALSE;
- }
+ PRBool editableMode = HasFlag(NODE_IS_EDITABLE);
+ if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
+ SetEditableFlag(!editableMode);
+
+ return EditingStateChanged();
}
- return rv;
+ return NS_OK;
}
nsresult
nsHTMLDocument::GetMidasCommandManager(nsICommandManager** aCmdMgr)
{
// initialize return value
NS_ENSURE_ARG_POINTER(aCmdMgr);
@@ -3977,29 +4202,31 @@ nsHTMLDocument::ConvertToMidasInternalCo
outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
}
outParam.Truncate();
}
else {
NS_ConvertUTF16toUTF8 convertedParam(inParam);
// check to see if we need to convert the parameter
- PRUint32 j;
- for (j = 0; j < MidasParamCount; ++j) {
- if (convertedParam.Equals(gMidasParamTable[j].incomingParamString,
- nsCaseInsensitiveCStringComparator())) {
- outParam.Assign(gMidasParamTable[j].internalParamString);
- break;
+ if (outCommandID.EqualsLiteral("cmd_paragraphState")) {
+ PRUint32 j;
+ for (j = 0; j < MidasParamCount; ++j) {
+ if (convertedParam.Equals(gMidasParamTable[j].incomingParamString,
+ nsCaseInsensitiveCStringComparator())) {
+ outParam.Assign(gMidasParamTable[j].internalParamString);
+ break;
+ }
}
+
+ return j != MidasParamCount;
}
-
- // if we didn't convert the parameter, just
- // pass through the parameter that was passed to us
- if (j == MidasParamCount)
+ else {
outParam.Assign(convertedParam);
+ }
}
}
} // end else for useNewParam (do convert existing param)
else {
// reset results if the command is not found in our table
outCommandID.SetLength(0);
outParam.SetLength(0);
outIsBoolean = PR_FALSE;
@@ -4064,22 +4291,22 @@ nsHTMLDocument::ExecCommand(const nsAStr
NS_ENSURE_ARG_POINTER(_retval);
// for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
// this might add some ugly JS dependencies?
*_retval = PR_FALSE;
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
// if they are requesting UI from us, let's fail since we have no UI
if (doShowUI)
- return NS_ERROR_NOT_IMPLEMENTED;
+ return NS_OK;
nsresult rv = NS_OK;
if (commandID.LowerCaseEqualsLiteral("gethtml"))
return NS_ERROR_FAILURE;
if (commandID.LowerCaseEqualsLiteral("cut") ||
(commandID.LowerCaseEqualsLiteral("copy"))) {
@@ -4100,17 +4327,17 @@ nsHTMLDocument::ExecCommand(const nsAStr
nsIDOMWindow *window = GetWindow();
if (!window)
return NS_ERROR_FAILURE;
nsCAutoString cmdToDispatch, paramStr;
PRBool isBool, boolVal;
if (!ConvertToMidasInternalCommand(commandID, value,
cmdToDispatch, paramStr, isBool, boolVal))
- return NS_ERROR_NOT_IMPLEMENTED;
+ return NS_OK;
if (!isBool && paramStr.IsEmpty()) {
rv = cmdMgr->DoCommand(cmdToDispatch.get(), nsnull, window);
} else {
// we have a command that requires a parameter, create params
nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
NS_COMMAND_PARAMS_CONTRACTID, &rv);
if (!cmdParams)
@@ -4139,32 +4366,32 @@ nsHTMLDocument::ExecCommand(const nsAStr
NS_IMETHODIMP
nsHTMLDocument::ExecCommandShowHelp(const nsAString & commandID,
PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_FALSE;
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
return NS_ERROR_NOT_IMPLEMENTED;
}
/* boolean queryCommandEnabled(in DOMString commandID); */
NS_IMETHODIMP
nsHTMLDocument::QueryCommandEnabled(const nsAString & commandID,
PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_FALSE;
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
// get command manager and dispatch command to our window if it's acceptable
nsCOMPtr<nsICommandManager> cmdMgr;
GetMidasCommandManager(getter_AddRefs(cmdMgr));
if (!cmdMgr)
return NS_ERROR_FAILURE;
@@ -4185,17 +4412,17 @@ nsHTMLDocument::QueryCommandEnabled(cons
NS_IMETHODIMP
nsHTMLDocument::QueryCommandIndeterm(const nsAString & commandID,
PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_FALSE;
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
// get command manager and dispatch command to our window if it's acceptable
nsCOMPtr<nsICommandManager> cmdMgr;
GetMidasCommandManager(getter_AddRefs(cmdMgr));
if (!cmdMgr)
return NS_ERROR_FAILURE;
@@ -4227,17 +4454,17 @@ nsHTMLDocument::QueryCommandIndeterm(con
/* boolean queryCommandState(in DOMString commandID); */
NS_IMETHODIMP
nsHTMLDocument::QueryCommandState(const nsAString & commandID, PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_FALSE;
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
// get command manager and dispatch command to our window if it's acceptable
nsCOMPtr<nsICommandManager> cmdMgr;
GetMidasCommandManager(getter_AddRefs(cmdMgr));
if (!cmdMgr)
return NS_ERROR_FAILURE;
@@ -4289,45 +4516,45 @@ nsHTMLDocument::QueryCommandState(const
NS_IMETHODIMP
nsHTMLDocument::QueryCommandSupported(const nsAString & commandID,
PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_FALSE;
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
return NS_ERROR_NOT_IMPLEMENTED;
}
/* DOMString queryCommandText(in DOMString commandID); */
NS_IMETHODIMP
nsHTMLDocument::QueryCommandText(const nsAString & commandID,
nsAString & _retval)
{
_retval.SetLength(0);
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
return NS_ERROR_NOT_IMPLEMENTED;
}
/* DOMString queryCommandValue(in DOMString commandID); */
NS_IMETHODIMP
nsHTMLDocument::QueryCommandValue(const nsAString & commandID,
nsAString &_retval)
{
_retval.SetLength(0);
// if editing is not on, bail
- if (!mEditingIsOn)
+ if (!IsEditingOn())
return NS_ERROR_FAILURE;
// get command manager and dispatch command to our window if it's acceptable
nsCOMPtr<nsICommandManager> cmdMgr;
GetMidasCommandManager(getter_AddRefs(cmdMgr));
if (!cmdMgr)
return NS_ERROR_FAILURE;
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -54,16 +54,18 @@
// Document.Write() related
#include "nsIWyciwygChannel.h"
#include "nsILoadGroup.h"
#include "nsNetUtil.h"
#include "nsICommandManager.h"
+class nsIEditor;
+class nsIEditorDocShell;
class nsIParser;
class nsIURI;
class nsIMarkupDocumentViewer;
class nsIDocumentCharsetInfo;
class nsICacheEntryDescriptor;
class nsHTMLDocument : public nsDocument,
public nsIHTMLDocument,
@@ -202,16 +204,23 @@ public:
#ifdef DEBUG
virtual nsresult CreateElem(nsIAtom *aName, nsIAtom *aPrefix,
PRInt32 aNamespaceID,
PRBool aDocumentDefaultType,
nsIContent** aResult);
#endif
+ nsresult ChangeContentEditableCount(nsIContent *aElement, PRInt32 aChange);
+
+ virtual PRBool IsEditingOn()
+ {
+ return mEditingState != eOff;
+ }
+
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLDocument, nsDocument)
protected:
nsresult GetBodySize(PRInt32* aWidth,
PRInt32* aHeight);
nsresult RegisterNamedItems(nsIContent *aContent);
nsresult UnregisterNamedItems(nsIContent *aContent);
@@ -360,17 +369,29 @@ protected:
nsresult GetMidasCommandManager(nsICommandManager** aCommandManager);
PRBool ConvertToMidasInternalCommand(const nsAString & inCommandID,
const nsAString & inParam,
nsACString& outCommandID,
nsACString& outParam,
PRBool& isBoolean,
PRBool& boolValue);
nsCOMPtr<nsICommandManager> mMidasCommandManager;
- PRBool mEditingIsOn;
+
+ nsresult TurnEditingOff();
+ nsresult EditingStateChanged();
+
+ PRUint32 mContentEditableCount;
+ enum EditingState {
+ eOff = 0,
+ eDesignMode,
+ eContentEditable
+ };
+ EditingState mEditingState;
+ PRPackedBool mScriptsEnabled;
+ PRPackedBool mPluginsEnabled;
nsresult DoClipboardSecurityCheck(PRBool aPaste);
static jsval sCutCopyInternal_id;
static jsval sPasteInternal_id;
// kNameSpaceID_None for good ol' HTML documents, and
// kNameSpaceID_XHTML for spiffy new XHTML documents.
// XXXbz should this be reset if someone manually calls
--- a/content/html/document/src/nsIHTMLDocument.h
+++ b/content/html/document/src/nsIHTMLDocument.h
@@ -50,18 +50,18 @@ class nsIDOMHTMLMapElement;
class nsHTMLStyleSheet;
class nsIStyleSheet;
class nsICSSLoader;
class nsIContent;
class nsIDOMHTMLBodyElement;
class nsIScriptElement;
#define NS_IHTMLDOCUMENT_IID \
-{ 0xcfe72003, 0xcc90, 0x4624, \
- { 0xb4, 0x1b, 0xc3, 0x14, 0x1d, 0x31, 0x7a, 0x71 } }
+{ 0xf6aa3582, 0x67c3, 0x4f42, \
+ { 0xb6, 0xee, 0x89, 0x19, 0x24, 0x5c, 0x15, 0x89 } }
/**
* HTML document extensions to nsIDocument.
*/
class nsIHTMLDocument : public nsISupports
{
public:
@@ -122,13 +122,30 @@ public:
*/
virtual nsContentList* GetForms() = 0;
/**
* Get the list of form controls in the document (all elements in
* the document that are of type nsIContent::eHTML_FORM_CONTROL).
*/
virtual nsContentList* GetFormControls() = 0;
+
+ /**
+ * Should be called when an element's editable changes as a result of
+ * changing its contentEditable attribute/property.
+ *
+ * @param aElement the element for which the contentEditable
+ * attribute/property was changed
+ * @param aChange +1 if the contentEditable attribute/property was changed to
+ * true, -1 if it was changed to false
+ */
+ virtual nsresult ChangeContentEditableCount(nsIContent *aElement,
+ PRInt32 aChange) = 0;
+
+ /**
+ * Returns whether the document is editable.
+ */
+ virtual PRBool IsEditingOn() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLDocument, NS_IHTMLDOCUMENT_IID)
#endif /* nsIHTMLDocument_h___ */
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1035,16 +1035,25 @@ nsXULElement::AfterSetAttr(PRInt32 aName
}
// Hide chrome if needed
if (aName == nsGkAtoms::hidechrome &&
mNodeInfo->Equals(nsGkAtoms::window)) {
HideWindowChrome(aValue && NS_LITERAL_STRING("true").Equals(*aValue));
}
+ // handle :read-only/:read-write
+ nsIDocument *document = GetCurrentDoc();
+ if (aName == nsGkAtoms::readonly && document) {
+ mozAutoDocUpdate(document, UPDATE_CONTENT_STATE, PR_TRUE);
+ document->ContentStatesChanged(this, nsnull,
+ NS_EVENT_STATE_MOZ_READONLY |
+ NS_EVENT_STATE_MOZ_READWRITE);
+ }
+
// XXX need to check if they're changing an event handler: if
// so, then we need to unhook the old one. Or something.
}
return nsGenericElement::AfterSetAttr(aNamespaceID, aName,
aValue, aNotify);
}
@@ -2093,16 +2102,32 @@ nsXULElement::AddPopupListener(nsIAtom*
NS_ENSURE_SUCCESS(rv, rv);
nsIXULPopupListener* listener = popupListener;
NS_ADDREF(listener);
target->AddEventListener(NS_LITERAL_STRING("mousedown"), eventListener, PR_FALSE);
target->AddEventListener(NS_LITERAL_STRING("contextmenu"), eventListener, PR_FALSE);
return NS_OK;
}
+PRInt32
+nsXULElement::IntrinsicState() const
+{
+ PRInt32 state = nsGenericElement::IntrinsicState();
+
+ const nsIAtom* tag = Tag();
+ if (GetNameSpaceID() == kNameSpaceID_XUL &&
+ (tag == nsGkAtoms::textbox || tag == nsGkAtoms::textarea) &&
+ !HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
+ state |= NS_EVENT_STATE_MOZ_READWRITE;
+ state &= ~NS_EVENT_STATE_MOZ_READONLY;
+ }
+
+ return state;
+}
+
//----------------------------------------------------------------------
nsGenericElement::nsAttrInfo
nsXULElement::GetAttrInfo(PRInt32 aNamespaceID, nsIAtom *aName) const
{
nsAttrInfo info(nsGenericElement::GetAttrInfo(aNamespaceID, aName));
if (!info.mValue) {
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -553,16 +553,17 @@ public:
// nsIDOMElement
NS_FORWARD_NSIDOMELEMENT(nsGenericElement::)
// nsIDOMXULElement
NS_DECL_NSIDOMXULELEMENT
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+ virtual PRInt32 IntrinsicState() const;
nsresult GetStyle(nsIDOMCSSStyleDeclaration** aStyle);
virtual void RecompileScriptEventListeners();
// This function should ONLY be used by BindToTree implementations.
// The function exists solely because XUL elements store the binding
// parent as a member instead of in the slots, as nsGenericElement does.
--- a/docshell/base/nsDocShellEditorData.cpp
+++ b/docshell/base/nsDocShellEditorData.cpp
@@ -68,17 +68,17 @@ nsDocShellEditorData::nsDocShellEditorDa
----------------------------------------------------------------------------*/
nsDocShellEditorData::~nsDocShellEditorData()
{
if (mEditingSession)
{
nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(mDocShell);
// This will eventually call nsDocShellEditorData::SetEditor(nsnull)
// which will call mEditorPreDestroy() and delete the editor
- mEditingSession->TearDownEditorOnWindow(domWindow);
+ mEditingSession->TearDownEditorOnWindow(domWindow, PR_TRUE);
}
else if (mEditor) // Should never have this w/o nsEditingSession!
{
mEditor->PreDestroy();
mEditor = nsnull; // explicit clear to make destruction order predictable
}
}
@@ -99,17 +99,18 @@ nsDocShellEditorData::MakeEditable(PRBoo
if (mEditor)
{
NS_WARNING("Destroying existing editor on frame");
mEditor->PreDestroy();
mEditor = nsnull;
}
- mMakeEditable = PR_TRUE;
+ if (inWaitForUriLoad)
+ mMakeEditable = PR_TRUE;
return NS_OK;
}
/*---------------------------------------------------------------------------
GetEditable
@@ -186,16 +187,18 @@ nsDocShellEditorData::SetEditor(nsIEdito
{
if (mEditor)
{
mEditor->PreDestroy();
mEditor = nsnull;
}
mEditor = inEditor; // owning addref
+ if (!mEditor)
+ mMakeEditable = PR_FALSE;
}
return NS_OK;
}
/*---------------------------------------------------------------------------
--- a/docshell/base/nsWebShell.cpp
+++ b/docshell/base/nsWebShell.cpp
@@ -767,17 +767,21 @@ nsWebShell::OnLinkClick(nsIContent* aCon
nsIInputStream* aPostDataStream,
nsIInputStream* aHeadersDataStream)
{
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
if (mFiredUnloadEvent) {
return NS_OK;
}
-
+
+ if (aContent->HasFlag(NODE_IS_EDITABLE)) {
+ return NS_OK;
+ }
+
nsCOMPtr<nsIRunnable> ev =
new OnLinkClickEvent(this, aContent, aURI, aTargetSpec,
aPostDataStream, aHeadersDataStream);
return NS_DispatchToCurrentThread(ev);
}
NS_IMETHODIMP
nsWebShell::OnLinkClickSync(nsIContent *aContent,
@@ -795,16 +799,20 @@ nsWebShell::OnLinkClickSync(nsIContent *
if (aRequest) {
*aRequest = nsnull;
}
if (mFiredUnloadEvent) {
return NS_OK;
}
+ if (aContent->HasFlag(NODE_IS_EDITABLE)) {
+ return NS_OK;
+ }
+
{
// defer to an external protocol handler if necessary...
nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
if (extProtService) {
nsCAutoString scheme;
aURI->GetScheme(scheme);
if (!scheme.IsEmpty()) {
// if the URL scheme does not correspond to an exposed protocol, then we
@@ -890,16 +898,20 @@ nsWebShell::OnLinkClickSync(nsIContent *
return rv;
}
NS_IMETHODIMP
nsWebShell::OnOverLink(nsIContent* aContent,
nsIURI* aURI,
const PRUnichar* aTargetSpec)
{
+ if (aContent->HasFlag(NODE_IS_EDITABLE)) {
+ return NS_OK;
+ }
+
nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIWebBrowserChrome> browserChrome;
if (!browserChrome2) {
browserChrome = do_GetInterface(mTreeOwner);
if (!browserChrome)
return rv;
--- a/dom/public/idl/html/nsIDOMNSHTMLElement.idl
+++ b/dom/public/idl/html/nsIDOMNSHTMLElement.idl
@@ -33,17 +33,17 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "domstubs.idl"
-[scriptable, uuid(b0a29b0a-ce2b-4cdf-b98a-4c7ed994d6e2)]
+[scriptable, uuid(eac0a4ee-2e4f-403c-9b77-5cf32cfb42f7)]
interface nsIDOMNSHTMLElement : nsISupports
{
readonly attribute long offsetTop;
readonly attribute long offsetLeft;
readonly attribute long offsetWidth;
readonly attribute long offsetHeight;
readonly attribute nsIDOMElement offsetParent;
attribute DOMString innerHTML;
@@ -55,16 +55,18 @@ interface nsIDOMNSHTMLElement : nsISuppo
readonly attribute long clientTop;
readonly attribute long clientLeft;
readonly attribute long clientHeight;
readonly attribute long clientWidth;
attribute long tabIndex;
+ attribute DOMString contentEditable;
+
void blur();
void focus();
// |top| is optional in JS, scriptability of this method is done in
// nsHTMLElementSH
void scrollIntoView(in boolean top);
attribute boolean spellcheck;
--- a/editor/composer/public/nsIEditingSession.idl
+++ b/editor/composer/public/nsIEditingSession.idl
@@ -38,17 +38,17 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "domstubs.idl"
interface nsIEditor;
-[scriptable, uuid(d39fd2b4-3978-45d2-a4be-ba448171b61b)]
+[scriptable, uuid(aee80d50-2065-4411-834d-0cadfb649a19)]
interface nsIEditingSession : nsISupports
{
/**
* Error codes when we fail to create an editor
* is placed in attribute editorStatus
*/
const long eEditorOK = 0;
@@ -63,18 +63,26 @@ interface nsIEditingSession : nsISupport
* Value is one of the above error codes
*/
readonly attribute unsigned long editorStatus;
/**
* Make this window editable
* @param aWindow nsIDOMWindow, the window the embedder needs to make editable
* @param aEditorType string, "html" "htmlsimple" "text" "textsimple"
+ * @param aMakeWholeDocumentEditable if PR_TRUE make the whole document in
+ * aWindow editable, otherwise it's the
+ * embedder who should make the document
+ * (or part of it) editable.
+ * @param aInteractive if PR_FALSE turn off scripting and plugins
*/
- void makeWindowEditable(in nsIDOMWindow window, in string aEditorType, in boolean doAfterUriLoad);
+ void makeWindowEditable(in nsIDOMWindow window, in string aEditorType,
+ in boolean doAfterUriLoad,
+ in boolean aMakeWholeDocumentEditable,
+ in boolean aInteractive);
/**
* Test whether a specific window has had its editable flag set; it may have an editor
* now, or will get one after the uri load.
*
* Use this, passing the content root window, to test if we've set up editing
* for this content.
*/
@@ -88,15 +96,15 @@ interface nsIEditingSession : nsISupport
/**
* Setup editor and related support objects
*/
void setupEditorOnWindow(in nsIDOMWindow window);
/**
* Destroy editor and related support objects
*/
- void tearDownEditorOnWindow(in nsIDOMWindow window);
+ void tearDownEditorOnWindow(in nsIDOMWindow window, in boolean aStopEditing);
void setEditorOnControllers(in nsIDOMWindow aWindow,
in nsIEditor aEditor);
};
--- a/editor/composer/src/nsEditingSession.cpp
+++ b/editor/composer/src/nsEditingSession.cpp
@@ -90,16 +90,17 @@
/*---------------------------------------------------------------------------
nsEditingSession
----------------------------------------------------------------------------*/
nsEditingSession::nsEditingSession()
: mDoneSetup(PR_FALSE)
, mCanCreateEditor(PR_FALSE)
+, mInteractive(PR_FALSE)
, mScriptsEnabled(PR_TRUE)
, mPluginsEnabled(PR_TRUE)
, mProgressListenerRegistered(PR_FALSE)
, mImageAnimationMode(0)
, mEditorFlags(0)
, mEditorStatus(eEditorOK)
, mBaseCommandControllerId(0)
, mDocStateControllerId(0)
@@ -123,55 +124,74 @@ NS_IMPL_ISUPPORTS3(nsEditingSession, nsI
nsISupportsWeakReference)
/*---------------------------------------------------------------------------
MakeWindowEditable
aEditorType string, "html" "htmlsimple" "text" "textsimple"
void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType,
- in boolean aDoAfterUriLoad);
+ in boolean aDoAfterUriLoad,
+ in boolean aMakeWholeDocumentEditable,
+ in boolean aInteractive);
----------------------------------------------------------------------------*/
#define DEFAULT_EDITOR_TYPE "html"
NS_IMETHODIMP
nsEditingSession::MakeWindowEditable(nsIDOMWindow *aWindow,
const char *aEditorType,
- PRBool aDoAfterUriLoad)
+ PRBool aDoAfterUriLoad,
+ PRBool aMakeWholeDocumentEditable,
+ PRBool aInteractive)
{
mEditorType.Truncate();
mEditorFlags = 0;
mWindowToBeEdited = do_GetWeakReference(aWindow);
// disable plugins
nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
if (!docShell) return NS_ERROR_FAILURE;
nsresult rv;
- // Disable JavaScript in this document:
- PRBool tmp;
- rv = docShell->GetAllowJavascript(&tmp);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (aMakeWholeDocumentEditable) {
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ rv = aWindow->GetDocument(getter_AddRefs(domDoc));
+ NS_ENSURE_SUCCESS(rv, rv);
- mScriptsEnabled = tmp;
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ doc->SetEditableFlag(PR_TRUE);
+ }
+
+ mInteractive = aInteractive;
- rv = docShell->SetAllowJavascript(PR_FALSE);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (!mInteractive) {
+ // Disable JavaScript in this document:
+ PRBool tmp;
+ rv = docShell->GetAllowJavascript(&tmp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mScriptsEnabled = tmp;
- // Disable plugins in this document:
- rv = docShell->GetAllowPlugins(&tmp);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = docShell->SetAllowJavascript(PR_FALSE);
+ NS_ENSURE_SUCCESS(rv, rv);
- mPluginsEnabled = tmp;
+ // Disable plugins in this document:
+ rv = docShell->GetAllowPlugins(&tmp);
+ NS_ENSURE_SUCCESS(rv, rv);
- rv = docShell->SetAllowPlugins(PR_FALSE);
- NS_ENSURE_SUCCESS(rv, rv);
+ mPluginsEnabled = tmp;
+
+ rv = docShell->SetAllowPlugins(PR_FALSE);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
// Always remove existing editor
- TearDownEditorOnWindow(aWindow);
+ TearDownEditorOnWindow(aWindow, PR_FALSE);
// Tells embedder that startup is in progress
mEditorStatus = eEditorCreationInProgress;
//temporary to set editor type here. we will need different classes soon.
if (!aEditorType)
aEditorType = DEFAULT_EDITOR_TYPE;
mEditorType = aEditorType;
@@ -210,17 +230,17 @@ nsEditingSession::MakeWindowEditable(nsI
if (!aDoAfterUriLoad)
{
rv = SetupEditorOnWindow(aWindow);
// mEditorStatus is set to the error reason
// Since this is used only when editing an existing page,
// it IS ok to destroy current editor
if (NS_FAILED(rv))
- TearDownEditorOnWindow(aWindow);
+ TearDownEditorOnWindow(aWindow, PR_FALSE);
}
return rv;
}
/*---------------------------------------------------------------------------
WindowIsEditable
@@ -356,16 +376,20 @@ nsEditingSession::SetupEditorOnWindow(ns
mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask |
nsIPlaintextEditor::eEditorEnableWrapHackMask;
}
else // Defaulted to html
{
needHTMLController = PR_TRUE;
}
+ if (mInteractive) {
+ mEditorFlags |= nsIPlaintextEditor::eEditorAllowInteraction;
+ }
+
// make the UI state maintainer
nsComposerCommandsUpdater *stateMaintainer;
NS_NEWXPCOM(stateMaintainer, nsComposerCommandsUpdater);
mStateMaintainer = NS_STATIC_CAST(nsISelectionListener*, stateMaintainer);
if (!mStateMaintainer) return NS_ERROR_OUT_OF_MEMORY;
// now init the state maintainer
@@ -385,23 +409,25 @@ nsEditingSession::SetupEditorOnWindow(ns
return NS_ERROR_FAILURE;
}
// Create editor and do other things
// only if we haven't found some error above,
nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
if (!docShell) return NS_ERROR_FAILURE;
- // Disable animation of images in this document:
- nsCOMPtr<nsIDOMWindowUtils> utils(do_GetInterface(aWindow));
- if (!utils) return NS_ERROR_FAILURE;
+ if (!mInteractive) {
+ // Disable animation of images in this document:
+ nsCOMPtr<nsIDOMWindowUtils> utils(do_GetInterface(aWindow));
+ if (!utils) return NS_ERROR_FAILURE;
- rv = utils->GetImageAnimationMode(&mImageAnimationMode);
- if (NS_FAILED(rv)) return rv;
- utils->SetImageAnimationMode(imgIContainer::kDontAnimMode);
+ rv = utils->GetImageAnimationMode(&mImageAnimationMode);
+ if (NS_FAILED(rv)) return rv;
+ utils->SetImageAnimationMode(imgIContainer::kDontAnimMode);
+ }
// create and set editor
nsCOMPtr<nsIEditorDocShell> editorDocShell = do_QueryInterface(docShell, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIEditor> editor = do_CreateInstance(classString, &rv);
if (NS_FAILED(rv)) return rv;
// set the editor on the docShell. The docShell now owns it.
@@ -475,20 +501,22 @@ nsEditingSession::SetupEditorOnWindow(ns
// This will trigger documentCreation notification
return editor->PostCreate();
}
/*---------------------------------------------------------------------------
TearDownEditorOnWindow
- void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
+ void tearDownEditorOnWindow (in nsIDOMWindow aWindow,
+ in boolean aStopEditing);
----------------------------------------------------------------------------*/
NS_IMETHODIMP
-nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow)
+nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow,
+ PRBool aStopEditing)
{
if (!mDoneSetup)
return NS_OK;
nsresult rv;
// Kill any existing reload timer
if (mLoadBlankDocTimer)
@@ -496,33 +524,17 @@ nsEditingSession::TearDownEditorOnWindow
mLoadBlankDocTimer->Cancel();
mLoadBlankDocTimer = nsnull;
}
nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
mDoneSetup = PR_FALSE;
- nsCOMPtr<nsIDOMDocument> dom_doc;
- aWindow->GetDocument(getter_AddRefs(dom_doc));
-
- nsCOMPtr<nsIDOMNSHTMLDocument> html_doc(do_QueryInterface(dom_doc));
- PRBool isMidas = PR_FALSE;
-
- if (html_doc) {
- nsAutoString designMode;
- html_doc->GetDesignMode(designMode);
-
- isMidas = designMode.EqualsLiteral("on");
- }
-
- if (isMidas) {
- // We're tearing down a midas editor, unregister callbacks since
- // we're all done editing here.
-
+ if (aStopEditing) {
nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
if (webProgress) {
webProgress->RemoveProgressListener(this);
mProgressListenerRegistered = PR_FALSE;
}
}
@@ -617,17 +629,17 @@ nsEditingSession::TearDownEditorOnWindow
}
// Clear IDs to trigger creation of new controllers
mBaseCommandControllerId = 0;
mDocStateControllerId = 0;
mHTMLCommandControllerId = 0;
}
- if (isMidas) {
+ if (aStopEditing && !mInteractive) {
// Make things the way they were before we started editing.
if (mScriptsEnabled) {
docShell->SetAllowJavascript(PR_TRUE);
}
if (mPluginsEnabled) {
docShell->SetAllowPlugins(PR_TRUE);
}
@@ -967,17 +979,17 @@ nsEditingSession::StartDocumentLoad(nsIW
NS_ENSURE_ARG_POINTER(aWebProgress);
// If we have an editor here, then we got a reload after making the editor.
// We need to blow it away and make a new one at the end of the load.
nsCOMPtr<nsIDOMWindow> domWindow;
aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
if (domWindow)
{
- TearDownEditorOnWindow(domWindow);
+ TearDownEditorOnWindow(domWindow, PR_FALSE);
}
if (aIsToBeMadeEditable)
mEditorStatus = eEditorCreationInProgress;
return NS_OK;
}
@@ -1037,37 +1049,55 @@ nsEditingSession::EndDocumentLoad(nsIWeb
// did someone set the flag to make this shell editable?
if (aIsToBeMadeEditable && mCanCreateEditor && editorDocShell)
{
PRBool makeEditable;
editorDocShell->GetEditable(&makeEditable);
if (makeEditable)
{
- mCanCreateEditor = PR_FALSE;
- rv = SetupEditorOnWindow(domWindow);
+ // do we already have an editor here?
+ nsCOMPtr<nsIEditor> editor;
+ rv = editorDocShell->GetEditor(getter_AddRefs(editor));
if (NS_FAILED(rv))
+ return rv;
+ if (!editor)
{
- // If we had an error, setup timer to load a blank page later
- if (mLoadBlankDocTimer)
+ mCanCreateEditor = PR_FALSE;
+ rv = SetupEditorOnWindow(domWindow);
+ if (NS_FAILED(rv))
{
- // Must cancel previous timer?
- mLoadBlankDocTimer->Cancel();
- mLoadBlankDocTimer = NULL;
+ // If we had an error, setup timer to load a blank page later
+ if (mLoadBlankDocTimer)
+ {
+ // Must cancel previous timer?
+ mLoadBlankDocTimer->Cancel();
+ mLoadBlankDocTimer = NULL;
+ }
+
+ mLoadBlankDocTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ mEditorStatus = eEditorCreationInProgress;
+ mLoadBlankDocTimer->InitWithFuncCallback(
+ nsEditingSession::TimerCallback,
+ (void*)docShell,
+ 10, nsITimer::TYPE_ONE_SHOT);
}
-
- mLoadBlankDocTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
- if (NS_FAILED(rv)) return rv;
+ }
- mEditorStatus = eEditorCreationInProgress;
- mLoadBlankDocTimer->InitWithFuncCallback(
- nsEditingSession::TimerCallback,
- (void*)docShell,
- 10, nsITimer::TYPE_ONE_SHOT);
- }
+ // XXX This should move somewhere else!
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ rv = domWindow->GetDocument(getter_AddRefs(domDoc));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ doc->SetEditableFlag(PR_TRUE);
}
}
return rv;
}
void
nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure)
@@ -1139,17 +1169,17 @@ nsEditingSession::EndPageLoad(nsIWebProg
// we need to make sure that all pages in editor (whether editable or not)
// can't refresh contents being edited
nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
if (refreshURI)
refreshURI->CancelRefreshURITimers();
#if 0
// Shouldn't we do this when we want to edit sub-frames?
- return MakeWindowEditable(domWindow, "html", PR_FALSE);
+ return MakeWindowEditable(domWindow, "html", PR_FALSE, mInteractive);
#else
return NS_OK;
#endif
}
#ifdef XP_MAC
#pragma mark -
--- a/editor/composer/src/nsEditingSession.h
+++ b/editor/composer/src/nsEditingSession.h
@@ -124,16 +124,18 @@ protected:
PRPackedBool mDoneSetup; // have we prepared for editing yet?
// Used to prevent double creation of editor because nsIWebProgressListener
// receives a STATE_STOP notification before the STATE_START
// for our document, so we wait for the STATE_START, then STATE_STOP
// before creating an editor
PRPackedBool mCanCreateEditor;
+ PRPackedBool mInteractive;
+
// True if scripts were enabled before the editor turned scripts
// off, otherwise false.
PRPackedBool mScriptsEnabled;
// True if plugins were enabled before the editor turned plugins
// off, otherwise false.
PRPackedBool mPluginsEnabled;
--- a/editor/composer/src/res/EditorOverride.css
+++ b/editor/composer/src/res/EditorOverride.css
@@ -30,16 +30,20 @@
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
+*|* {
+ -moz-user-modify: read-write;
+}
+
/* Styles to alter look of things in the Editor content window
* that should NOT be removed when we display in completely WYSIWYG
* "Browser Preview" mode.
* Anything that should change, like appearance of table borders
* and Named Anchors, should be placed in EditorContent.css instead of here.
*/
/* Primary cursor is text I-beam */
--- a/editor/docs/Editor_Embedding_Guide.html
+++ b/editor/docs/Editor_Embedding_Guide.html
@@ -13,35 +13,38 @@ 11/5/02 original by Michael Judge <<a
3/27/03 updates by Kathleen Brade <<a
href="mailto:brade@netscape.com">brade@netscape.com</a>><br>
<h2>In the Beginning there is MakeEditable</h2>
<p>Given an nsIWebBrowser instance, get a nsIDOMWindow from the
GetContentDOMWindow call. Then simply call
nsIWebBrowser->do_GetInterface on the nsIWebBrowser to retrieve the
nsIEditingSession from it. From there you call
editingSession->MakeWindowEditable(domWindow, editortype,
-PR_TRUE); The first parameter is the nsIDOMWindow you
-just retrieved, the second is the editor type you want to create and the
+PR_TRUE, PR_FALSE); The first parameter is the nsIDOMWindow
+you just retrieved, the second is the editor type you want to create and the
third is whether you want the window editable immediately or when the
-document is done loading. In calling this method the editor is
+document is done loading, the fourth is whether you want the editor to make
+the whole document editable, the fifth is whether you want to turn of
+scripts, plugins, ... In calling this method the editor is
created underneath and the event listeners etc. are all prepared.<br>
</p>
<p><i> nsCOMPtr<nsIDOMWindow> domWindow;<br>
nsresult rv =
nsIWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));<br>
if (NS_FAILED(rv)) return NS_ERROR_FAILURE; // we
are not setup??!!<br>
</i></p>
<p><i> nsCOMPtr<nsIEditingSession>
editingSession;<br>
nsIWebBrowser->do_GetInterface(getter_AddRefs(editingSession));<br>
if (editingSession)<br>
-editingSession->MakeWindowEditable(domWindow, "html", PR_TRUE);</i></p>
+editingSession->MakeWindowEditable(domWindow, "html", PR_TRUE,
+PR_FALSE, PR_TRUE, PR_FALSE);</i></p>
<p>The valid editor types are:<br>
</p>
<ul>
<li>"text" (similar to NotePad or a textarea; does not allow for html)</li>
<li>"textmail" (similar to "text" but html can be inserted; intended
for plaintext mail usage and handling of citations)</li>
<li>"html" (this is the default type if no type is specified; it
allows for all html tags to be inserted)<br>
--- a/editor/idl/nsIEditor.idl
+++ b/editor/idl/nsIEditor.idl
@@ -57,17 +57,17 @@ interface nsIInlineSpellChecker;
%{C++
class nsIPresShell;
typedef short EDirection;
%}
[ptr] native nsIPresShellPtr(nsIPresShell);
-[scriptable, uuid(470e18e4-2e82-48de-8850-7474cdbbd97d)]
+[scriptable, uuid(dc81f464-89dd-47bf-bf21-10df3b65b956)]
interface nsIEditor : nsISupports
{
%{C++
typedef short EDirection;
%}
const short eNone = 0;
const short eNext = 1;
@@ -557,9 +557,12 @@ interface nsIEditor : nsISupports
*/
void dumpContentTree();
/** Dumps a text representation of the content tree to standard out */
void debugDumpContent() ;
/* Run unit tests. Noop in optimized builds */
void debugUnitTests(out long outNumTests, out long outNumTestsFailed);
+
+ /* checks if a node is read-only or not */
+ [notxpcom] boolean isModifiableNode(in nsIDOMNode aNode);
};
--- a/editor/idl/nsIPlaintextEditor.idl
+++ b/editor/idl/nsIPlaintextEditor.idl
@@ -34,44 +34,33 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIDOMKeyEvent;
-[scriptable, uuid(28dbb4d0-5fea-43f4-aca7-344fc2ecc4f9)]
+[scriptable, uuid(35d74f2b-3d03-43c5-8ace-9d90c3e31244)]
interface nsIPlaintextEditor : nsISupports
{
- /* the bits in an editor behavior mask. */
- const short eEditorPlaintextBit = 0; /* only plain text entry is allowed via events */
- const short eEditorSingleLineBit = 1; /* enter key and CR-LF handled specially */
- const short eEditorPasswordBit = 2; /* text is not entered into content, only a representative character */
- const short eEditorReadonlyBit = 3; /* editing events are disabled. Editor may still accept focus. */
- const short eEditorDisabledBit = 4; /* all events are disabled (like scrolling). Editor will not accept focus. */
- const short eEditorFilterInputBit = 5; /* text input is limited to certain character types, use mFilter */
- const short eEditorMailBit = 6; /* use mail-compose editting rules */
- const short eEditorUseAsyncUpdatesBit = 7; /* prevent immediate reflows and view refreshes */
- const short eEditorEnableWrapHackBit = 8; /* allow the editor to set font: monospace on the root node */
- const short eEditorWidgetBit = 9; /* bit for widgets */
- const short eEditorNoCSSBit = 10; /* this HTML editor should not create css styles */
-
- const long eEditorPlaintextMask = 1;
- const long eEditorSingleLineMask = 2;
- const long eEditorPasswordMask = 4;
- const long eEditorReadonlyMask = 8;
- const long eEditorDisabledMask = 16;
- const long eEditorFilterInputMask = 32;
- const long eEditorMailMask = 64;
- const long eEditorUseAsyncUpdatesMask = 128;
- const long eEditorEnableWrapHackMask = 256;
- const long eEditorWidgetMask = 512;
- const long eEditorNoCSSMask = 1024;
+ // XXX Why aren't these in nsIEditor?
+ const long eEditorPlaintextMask = 0x0001; /* only plain text entry is allowed via events */
+ const long eEditorSingleLineMask = 0x0002; /* enter key and CR-LF handled specially */
+ const long eEditorPasswordMask = 0x0004; /* text is not entered into content, only a representative character */
+ const long eEditorReadonlyMask = 0x0008; /* editing events are disabled. Editor may still accept focus. */
+ const long eEditorDisabledMask = 0x0010; /* all events are disabled (like scrolling). Editor will not accept focus. */
+ const long eEditorFilterInputMask = 0x0020; /* text input is limited to certain character types, use mFilter */
+ const long eEditorMailMask = 0x0040; /* use mail-compose editing rules */
+ const long eEditorUseAsyncUpdatesMask = 0x0080; /* prevent immediate reflows and view refreshes */
+ const long eEditorEnableWrapHackMask = 0x0100; /* allow the editor to set font: monospace on the root node */
+ const long eEditorWidgetMask = 0x0200; /* bit for widgets */
+ const long eEditorNoCSSMask = 0x0400; /* this HTML editor should not create css styles */
+ const long eEditorAllowInteraction = 0x0800; /* */
/*
* The valid values for newlines handling.
* Can't change the values unless we remove
* use of the pref.
*/
const long eNewlinesPasteIntact = 0;
const long eNewlinesPasteToFirst = 1;
--- a/editor/libeditor/base/DeleteElementTxn.cpp
+++ b/editor/libeditor/base/DeleteElementTxn.cpp
@@ -53,36 +53,44 @@ DeleteElementTxn::DeleteElementTxn()
: EditTxn()
,mElement()
,mParent()
,mRefNode()
,mRangeUpdater(nsnull)
{
}
-NS_IMETHODIMP DeleteElementTxn::Init(nsIDOMNode *aElement,
+NS_IMETHODIMP DeleteElementTxn::Init(nsIEditor *aEditor,
+ nsIDOMNode *aElement,
nsRangeUpdater *aRangeUpdater)
{
- if (!aElement) return NS_ERROR_NULL_POINTER;
+ if (!aEditor || !aElement) return NS_ERROR_NULL_POINTER;
+ mEditor = aEditor;
mElement = do_QueryInterface(aElement);
+ nsresult result = mElement->GetParentNode(getter_AddRefs(mParent));
+ if (NS_FAILED(result)) { return result; }
+
+ // do nothing if the parent is read-only
+ if (mParent && !mEditor->IsModifiableNode(mParent)) {
+ return NS_ERROR_FAILURE;
+ }
+
mRangeUpdater = aRangeUpdater;
return NS_OK;
}
NS_IMETHODIMP DeleteElementTxn::DoTransaction(void)
{
#ifdef NS_DEBUG
if (gNoisy) { printf("%p Do Delete Element element = %p\n", this, mElement.get()); }
#endif
if (!mElement) return NS_ERROR_NOT_INITIALIZED;
- nsresult result = mElement->GetParentNode(getter_AddRefs(mParent));
- if (NS_FAILED(result)) { return result; }
if (!mParent) { return NS_OK; } // this is a no-op, there's no parent to delete mElement from
#ifdef NS_DEBUG
// begin debug output
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mElement);
nsAutoString elementTag(NS_LITERAL_STRING("text node"));
if (element)
element->GetTagName(elementTag);
@@ -100,17 +108,17 @@ NS_IMETHODIMP DeleteElementTxn::DoTransa
NS_Free(c);
NS_Free(p);
}
// end debug output
#endif
// remember which child mElement was (by remembering which child was next)
- result = mElement->GetNextSibling(getter_AddRefs(mRefNode)); // can return null mRefNode
+ nsresult result = mElement->GetNextSibling(getter_AddRefs(mRefNode)); // can return null mRefNode
// give range updater a chance. SelAdjDeleteNode() needs to be called *before*
// we do the action, unlike some of the other nsRangeStore update methods.
if (mRangeUpdater)
mRangeUpdater->SelAdjDeleteNode(mElement);
nsCOMPtr<nsIDOMNode> resultNode;
return mParent->RemoveChild(mElement, getter_AddRefs(resultNode));
--- a/editor/libeditor/base/DeleteElementTxn.h
+++ b/editor/libeditor/base/DeleteElementTxn.h
@@ -36,16 +36,17 @@
* ***** END LICENSE BLOCK ***** */
#ifndef DeleteElementTxn_h__
#define DeleteElementTxn_h__
#include "EditTxn.h"
#include "nsIDOMNode.h"
+#include "nsIEditor.h"
#include "nsCOMPtr.h"
#define DELETE_ELEMENT_TXN_CID \
{/* 6fd77770-ac49-11d2-86d8-000064657374 */ \
0x6fd77770, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsRangeUpdater;
@@ -57,17 +58,17 @@ class DeleteElementTxn : public EditTxn
{
public:
static const nsIID& GetCID() { static const nsIID iid = DELETE_ELEMENT_TXN_CID; return iid; }
/** initialize the transaction.
* @param aElement the node to delete
*/
- NS_IMETHOD Init(nsIDOMNode *aElement, nsRangeUpdater *aRangeUpdater);
+ NS_IMETHOD Init(nsIEditor *aEditor, nsIDOMNode *aElement, nsRangeUpdater *aRangeUpdater);
private:
DeleteElementTxn();
public:
NS_DECL_EDITTXN
NS_IMETHOD RedoTransaction();
@@ -78,16 +79,19 @@ protected:
nsCOMPtr<nsIDOMNode> mElement;
/** parent of node to delete */
nsCOMPtr<nsIDOMNode> mParent;
/** next sibling to remember for undo/redo purposes */
nsCOMPtr<nsIDOMNode> mRefNode;
+ /** the editor for this transaction */
+ nsIEditor* mEditor;
+
/** range updater object */
nsRangeUpdater *mRangeUpdater;
friend class TransactionFactory;
};
#endif
--- a/editor/libeditor/base/DeleteRangeTxn.cpp
+++ b/editor/libeditor/base/DeleteRangeTxn.cpp
@@ -83,16 +83,27 @@ NS_IMETHODIMP DeleteRangeTxn::Init(nsIEd
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed.");
result = aRange->GetStartOffset(&mStartOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed.");
result = aRange->GetEndOffset(&mEndOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed.");
result = aRange->GetCommonAncestorContainer(getter_AddRefs(mCommonParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed.");
+ if (!mEditor->IsModifiableNode(mStartParent)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mStartParent!=mEndParent &&
+ (!mEditor->IsModifiableNode(mEndParent) ||
+ !mEditor->IsModifiableNode(mCommonParent)))
+ {
+ return NS_ERROR_FAILURE;
+ }
+
#ifdef NS_DEBUG
{
PRUint32 count;
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(mStartParent);
if (textNode)
textNode->GetLength(&count);
else
{
@@ -231,18 +242,19 @@ DeleteRangeTxn::CreateTxnsToDeleteBetwee
if (NS_FAILED(result)) return result;
if (!txn) return NS_ERROR_NULL_POINTER;
PRInt32 numToDel;
if (aStartOffset==aEndOffset)
numToDel = 1;
else
numToDel = aEndOffset-aStartOffset;
- txn->Init(mEditor, textNode, aStartOffset, numToDel, mRangeUpdater);
- AppendChild(txn);
+ result = txn->Init(mEditor, textNode, aStartOffset, numToDel, mRangeUpdater);
+ if (NS_SUCCEEDED(result))
+ AppendChild(txn);
NS_RELEASE(txn);
}
else
{
nsCOMPtr<nsIDOMNodeList> children;
result = aStartParent->GetChildNodes(getter_AddRefs(children));
if (NS_FAILED(result)) return result;
if (!children) return NS_ERROR_NULL_POINTER;
@@ -260,18 +272,19 @@ DeleteRangeTxn::CreateTxnsToDeleteBetwee
if (NS_FAILED(result)) return result;
if (!child) return NS_ERROR_NULL_POINTER;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn);
if (NS_FAILED(result)) return result;
if (!txn) return NS_ERROR_NULL_POINTER;
- txn->Init(child, mRangeUpdater);
- AppendChild(txn);
+ result = txn->Init(mEditor, child, mRangeUpdater);
+ if (NS_SUCCEEDED(result))
+ AppendChild(txn);
NS_RELEASE(txn);
}
}
return result;
}
NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent,
PRUint32 aOffset,
@@ -297,44 +310,46 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxns
if (numToDelete)
{
DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)&txn);
if (NS_FAILED(result)) return result;
if (!txn) return NS_ERROR_NULL_POINTER;
- txn->Init(mEditor, textNode, start, numToDelete, mRangeUpdater);
- AppendChild(txn);
+ result = txn->Init(mEditor, textNode, start, numToDelete, mRangeUpdater);
+ if (NS_SUCCEEDED(result))
+ AppendChild(txn);
NS_RELEASE(txn);
}
}
return result;
}
NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween()
{
nsCOMPtr<nsIContentIterator> iter = do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1");
if (!iter) return NS_ERROR_NULL_POINTER;
nsresult result = iter->Init(mRange);
if (NS_FAILED(result)) return result;
- while (!iter->IsDone())
+ while (!iter->IsDone() && NS_SUCCEEDED(result))
{
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(iter->GetCurrentNode());
if (!node)
return NS_ERROR_NULL_POINTER;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn);
if (NS_FAILED(result)) return result;
if (!txn) return NS_ERROR_NULL_POINTER;
- txn->Init(node, mRangeUpdater);
- AppendChild(txn);
+ result = txn->Init(mEditor, node, mRangeUpdater);
+ if (NS_SUCCEEDED(result))
+ AppendChild(txn);
NS_RELEASE(txn);
iter->Next();
}
return result;
}
--- a/editor/libeditor/base/DeleteTextTxn.cpp
+++ b/editor/libeditor/base/DeleteTextTxn.cpp
@@ -60,16 +60,21 @@ NS_IMETHODIMP DeleteTextTxn::Init(nsIEdi
PRUint32 aNumCharsToDelete,
nsRangeUpdater *aRangeUpdater)
{
NS_ASSERTION(aEditor&&aElement, "bad arg");
if (!aEditor || !aElement) { return NS_ERROR_NULL_POINTER; }
mEditor = aEditor;
mElement = do_QueryInterface(aElement);
+ // do nothing if the node is read-only
+ if (!mEditor->IsModifiableNode(mElement)) {
+ return NS_ERROR_FAILURE;
+ }
+
mOffset = aOffset;
mNumCharsToDelete = aNumCharsToDelete;
NS_ASSERTION(0!=aNumCharsToDelete, "bad arg, numCharsToDelete");
PRUint32 count;
aElement->GetLength(&count);
NS_ASSERTION(count>=aNumCharsToDelete, "bad arg, numCharsToDelete. Not enough characters in node");
NS_ASSERTION(count>=aOffset+aNumCharsToDelete, "bad arg, numCharsToDelete. Not enough characters in node");
mDeletedText.Truncate();
--- a/editor/libeditor/base/JoinElementTxn.cpp
+++ b/editor/libeditor/base/JoinElementTxn.cpp
@@ -52,16 +52,22 @@ JoinElementTxn::JoinElementTxn()
NS_IMETHODIMP JoinElementTxn::Init(nsEditor *aEditor,
nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode)
{
NS_PRECONDITION((aEditor && aLeftNode && aRightNode), "null arg");
if (!aEditor || !aLeftNode || !aRightNode) { return NS_ERROR_NULL_POINTER; }
mEditor = aEditor;
mLeftNode = do_QueryInterface(aLeftNode);
+ nsCOMPtr<nsIDOMNode>leftParent;
+ nsresult result = mLeftNode->GetParentNode(getter_AddRefs(leftParent));
+ if (NS_FAILED(result)) return result;
+ if (!mEditor->IsModifiableNode(leftParent)) {
+ return NS_ERROR_FAILURE;
+ }
mRightNode = do_QueryInterface(aRightNode);
mOffset=0;
return NS_OK;
}
// After DoTransaction() and RedoTransaction(), the left node is removed from the content tree and right node remains.
NS_IMETHODIMP JoinElementTxn::DoTransaction(void)
{
--- a/editor/libeditor/base/nsEditPropertyAtomList.h
+++ b/editor/libeditor/base/nsEditPropertyAtomList.h
@@ -161,16 +161,17 @@ EDITOR_ATOM(cssRight, "right")
EDITOR_ATOM(cssTextAlign, "text-align")
EDITOR_ATOM(cssTextDecoration, "text-decoration")
EDITOR_ATOM(cssTop, "top")
EDITOR_ATOM(cssVerticalAlign, "vertical-align")
EDITOR_ATOM(cssWhitespace, "white-space")
EDITOR_ATOM(cssWidth, "width")
EDITOR_ATOM(cssZIndex, "z-index")
+EDITOR_ATOM(cssMozUserModify, "-moz-user-modify")
EDITOR_ATOM(cssMozUserSelect, "-moz-user-select")
EDITOR_ATOM(mozdirty, "_moz_dirty")
EDITOR_ATOM(cssPxUnit, "px")
EDITOR_ATOM(cssEmUnit, "em")
EDITOR_ATOM(cssCmUnit, "cm")
EDITOR_ATOM(cssPercentUnit, "%")
EDITOR_ATOM(cssInUnit, "in")
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -3788,18 +3788,18 @@ nsEditor::IsTextInDirtyFrameVisible(nsID
PRBool
nsEditor::IsEditable(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
nsCOMPtr<nsIPresShell> shell;
GetPresShell(getter_AddRefs(shell));
if (!shell) return PR_FALSE;
- if (IsMozEditorBogusNode(aNode)) return PR_FALSE;
-
+ if (IsMozEditorBogusNode(aNode) || !IsModifiableNode(aNode)) return PR_FALSE;
+
// see if it has a frame. If so, we'll edit it.
// special case for textnodes: frame must have width.
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
if (content)
{
nsIFrame *resultFrame = shell->GetPrimaryFrameFor(content);
if (!resultFrame) // if it has no frame, it is not editable
return PR_FALSE;
@@ -4745,17 +4745,17 @@ NS_IMETHODIMP nsEditor::CreateTxnForInse
NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement,
DeleteElementTxn ** aTxn)
{
nsresult result = NS_ERROR_NULL_POINTER;
if (nsnull != aElement)
{
result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn);
if (NS_SUCCEEDED(result)) {
- result = (*aTxn)->Init(aElement, &mRangeUpdater);
+ result = (*aTxn)->Init(this, aElement, &mRangeUpdater);
}
}
return result;
}
NS_IMETHODIMP
nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
IMETextTxn ** aTxn)
@@ -5366,8 +5366,14 @@ nsEditor::DumpNode(nsIDOMNode *aNode, PR
textNode->GetData(str);
nsCAutoString cstr;
LossyCopyUTF16toASCII(str, cstr);
cstr.ReplaceChar('\n', ' ');
printf("<textnode> %s\n", cstr.get());
}
}
#endif
+
+PRBool
+nsEditor::IsModifiableNode(nsIDOMNode *aNode)
+{
+ return PR_TRUE;
+}
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -580,16 +580,61 @@ nsHTMLEditRules::WillDoAction(nsISelecti
printf("nsHTMLEditRules::WillDoAction action = %d\n", aInfo->action);
#endif
*aCancel = PR_FALSE;
*aHandled = PR_FALSE;
// my kingdom for dynamic cast
nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo);
+
+ // Deal with actions for which we don't need to check whether the selection is
+ // editable.
+ if (info->action == kOutputText) {
+ return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
+ }
+
+ nsCOMPtr<nsIDOMRange> domRange;
+ nsresult rv = aSelection->GetRangeAt(0, getter_AddRefs(domRange));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMNode> selStartNode;
+ rv = domRange->GetStartContainer(getter_AddRefs(selStartNode));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mHTMLEditor->IsModifiableNode(selStartNode))
+ {
+ *aCancel = PR_TRUE;
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDOMNode> selEndNode;
+ rv = domRange->GetEndContainer(getter_AddRefs(selEndNode));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (selStartNode != selEndNode)
+ {
+ if (!mHTMLEditor->IsModifiableNode(selEndNode))
+ {
+ *aCancel = PR_TRUE;
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIRange> range = do_QueryInterface(domRange);
+ nsCOMPtr<nsIDOMNode> ancestor =
+ do_QueryInterface(range->GetCommonAncestor());
+ if (!mHTMLEditor->IsModifiableNode(ancestor))
+ {
+ *aCancel = PR_TRUE;
+
+ return NS_OK;
+ }
+ }
switch (info->action)
{
case kInsertText:
case kInsertTextIME:
return WillInsertText(info->action,
aSelection,
aCancel,
@@ -1564,16 +1609,23 @@ nsHTMLEditRules::WillInsertBreak(nsISele
if (IsBlockNode(node))
blockParent = node;
else
blockParent = mHTMLEditor->GetBlockNodeParent(node);
if (!blockParent) return NS_ERROR_FAILURE;
+ // do nothing if the node is read-only
+ if (!mHTMLEditor->IsModifiableNode(blockParent))
+ {
+ *aCancel = PR_TRUE;
+ return NS_OK;
+ }
+
// if block is empty, populate with br.
// (for example, imagine a div that contains the word "text". the user selects
// "text" and types return. "text" is deleted leaving an empty block. we want
// to put in one br to make block have a line. then code further below will put
// in a second br.)
PRBool isEmpty;
res = IsEmptyBlock(blockParent, &isEmpty);
if (isEmpty)
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -125,16 +125,17 @@
#include "nsEditorUtils.h"
#include "nsWSRunObject.h"
#include "nsHTMLObjectResizer.h"
#include "nsIFrame.h"
#include "nsIView.h"
#include "nsIWidget.h"
#include "nsIParserService.h"
+#include "nsIEventStateManager.h"
// Some utilities to handle annoying overloading of "A" tag for link and named anchor
static char hrefText[] = "href";
static char anchorTxt[] = "anchor";
static char namedanchorText[] = "namedanchor";
nsIRangeUtils* nsHTMLEditor::sRangeHelper;
@@ -297,33 +298,35 @@ nsHTMLEditor::Init(nsIDOMDocument *aDoc,
delete mHTMLCSSUtils;
result = NS_NewHTMLCSSUtils(&mHTMLCSSUtils);
if (NS_FAILED(result)) { return result; }
mHTMLCSSUtils->Init(this);
// disable links
nsPresContext *context = aPresShell->GetPresContext();
if (!context) return NS_ERROR_NULL_POINTER;
- if (!(mFlags & eEditorPlaintextMask)) {
+ if (!(mFlags & (eEditorPlaintextMask | eEditorAllowInteraction))) {
mLinkHandler = context->GetLinkHandler();
context->SetLinkHandler(nsnull);
}
// init the type-in state
mTypeInState = new TypeInState();
if (!mTypeInState) {return NS_ERROR_NULL_POINTER;}
NS_ADDREF(mTypeInState);
// init the selection listener for image resizing
mSelectionListenerP = new ResizerSelectionListener(this);
if (!mSelectionListenerP) {return NS_ERROR_NULL_POINTER;}
- // ignore any errors from this in case the file is missing
- AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/EditorOverride.css"));
+ if (!(mFlags & eEditorAllowInteraction)) {
+ // ignore any errors from this in case the file is missing
+ AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/EditorOverride.css"));
+ }
nsCOMPtr<nsISelection>selection;
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) { return result; }
if (selection)
{
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
nsCOMPtr<nsISelectionListener>listener;
@@ -3879,38 +3882,60 @@ nsHTMLEditor::GetEmbeddedObjects(nsISupp
#ifdef XP_MAC
#pragma mark -
#pragma mark nsIEditor overrides
#pragma mark -
#endif
NS_IMETHODIMP nsHTMLEditor::DeleteNode(nsIDOMNode * aNode)
{
+ // do nothing if the node is read-only
+ if (!IsModifiableNode(aNode)) {
+ return NS_ERROR_FAILURE;
+ }
+
nsCOMPtr<nsIDOMNode> selectAllNode = FindUserSelectAllNode(aNode);
if (selectAllNode)
{
return nsEditor::DeleteNode(selectAllNode);
}
return nsEditor::DeleteNode(aNode);
}
NS_IMETHODIMP nsHTMLEditor::DeleteText(nsIDOMCharacterData *aTextNode,
PRUint32 aOffset,
PRUint32 aLength)
{
+ // do nothing if the node is read-only
+ if (!IsModifiableNode(aTextNode)) {
+ return NS_ERROR_FAILURE;
+ }
+
nsCOMPtr<nsIDOMNode> selectAllNode = FindUserSelectAllNode(aTextNode);
if (selectAllNode)
{
return nsEditor::DeleteNode(selectAllNode);
}
return nsEditor::DeleteText(aTextNode, aOffset, aLength);
}
+NS_IMETHODIMP nsHTMLEditor::InsertTextImpl(const nsAString& aStringToInsert,
+ nsCOMPtr<nsIDOMNode> *aInOutNode,
+ PRInt32 *aInOutOffset,
+ nsIDOMDocument *aDoc)
+{
+ // do nothing if the node is read-only
+ if (!IsModifiableNode(*aInOutNode)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return nsEditor::InsertTextImpl(aStringToInsert, aInOutNode, aInOutOffset, aDoc);
+}
#ifdef XP_MAC
#pragma mark -
#pragma mark support utils
#pragma mark -
#endif
/* This routine examines aNode and it's ancestors looking for any node which has the
@@ -3942,16 +3967,24 @@ nsCOMPtr<nsIDOMNode> nsHTMLEditor::FindU
{
node = nsnull;
}
}
return resultNode;
}
+NS_IMETHODIMP_(PRBool)
+nsHTMLEditor::IsModifiableNode(nsIDOMNode *aNode)
+{
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+
+ return !content || !(content->IntrinsicState() & NS_EVENT_STATE_MOZ_READONLY);
+}
+
static nsresult SetSelectionAroundHeadChildren(nsCOMPtr<nsISelection> aSelection, nsWeakPtr aDocWeak)
{
nsresult res = NS_OK;
// Set selection around <head> node
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(aDocWeak);
if (!doc) return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMNodeList>nodeList;
@@ -4199,16 +4232,64 @@ nsHTMLEditor::SelectEntireDocument(nsISe
{
// if its empty dont select entire doc - that would select the bogus node
return aSelection->Collapse(rootElement, 0);
}
return nsEditor::SelectEntireDocument(aSelection);
}
+static nsIContent*
+FindEditableRoot(nsIContent *aContent)
+{
+ nsIDocument *document = aContent->GetCurrentDoc();
+ if (!document || document->HasFlag(NODE_IS_EDITABLE) ||
+ !aContent->HasFlag(NODE_IS_EDITABLE)) {
+ return nsnull;
+ }
+
+ nsIContent *parent, *content = aContent;
+ while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
+ content = parent;
+ }
+
+ return content;
+}
+
+NS_IMETHODIMP
+nsHTMLEditor::SelectAll()
+{
+ ForceCompositionEnd();
+
+ nsresult rv;
+ nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISelection> selection;
+ rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+ getter_AddRefs(selection));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMNode> anchorNode;
+ rv = selection->GetAnchorNode(getter_AddRefs(anchorNode));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsIContent *rootContent = FindEditableRoot(anchorContent);
+ if (!rootContent) {
+ return SelectEntireDocument(selection);
+ }
+
+ nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return selection->SelectAllChildren(rootElement);
+}
#ifdef XP_MAC
#pragma mark -
#pragma mark Random methods
#pragma mark -
#endif
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -358,16 +358,23 @@ public:
NS_IMETHOD CollapseAdjacentTextNodes(nsIDOMRange *aInRange);
virtual PRBool NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2);
NS_IMETHODIMP DeleteNode(nsIDOMNode * aNode);
NS_IMETHODIMP DeleteText(nsIDOMCharacterData *aTextNode,
PRUint32 aOffset,
PRUint32 aLength);
+ NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert,
+ nsCOMPtr<nsIDOMNode> *aInOutNode,
+ PRInt32 *aInOutOffset,
+ nsIDOMDocument *aDoc);
+ NS_IMETHOD_(PRBool) IsModifiableNode(nsIDOMNode *aNode);
+
+ NS_IMETHOD SelectAll();
/* ------------ nsICSSLoaderObserver -------------- */
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aWasAlternate,
nsresult aStatus);
/* ------------ Utility Routines, not part of public API -------------- */
NS_IMETHOD TypedText(const nsAString& aString, PRInt32 aAction);
nsresult InsertNodeAtPoint( nsIDOMNode *aNode,
--- a/editor/libeditor/text/nsEditorEventListeners.cpp
+++ b/editor/libeditor/text/nsEditorEventListeners.cpp
@@ -63,16 +63,17 @@
#include "nsIDragService.h"
#include "nsIDragSession.h"
#include "nsIContent.h"
#include "nsISupportsPrimitives.h"
#include "nsIDOMNSRange.h"
#include "nsEditorUtils.h"
#include "nsIDOMEventTarget.h"
#include "nsIEventStateManager.h"
+#include "nsISelectionPrivate.h"
//#define DEBUG_IME
/*
* nsTextEditorKeyListener implementation
*/
NS_IMPL_ISUPPORTS2(nsTextEditorKeyListener, nsIDOMEventListener, nsIDOMKeyListener)
@@ -218,17 +219,18 @@ nsTextEditorKeyListener::KeyPress(nsIDOM
mEditor->DeleteSelection(nsIEditor::eNext);
aKeyEvent->PreventDefault(); // consumed
return NS_OK;
break;
case nsIDOMKeyEvent::DOM_VK_TAB:
if ((flags & nsIPlaintextEditor::eEditorSingleLineMask) ||
(flags & nsIPlaintextEditor::eEditorPasswordMask) ||
- (flags & nsIPlaintextEditor::eEditorWidgetMask))
+ (flags & nsIPlaintextEditor::eEditorWidgetMask) ||
+ (flags & nsIPlaintextEditor::eEditorAllowInteraction))
return NS_OK; // let it be used for focus switching
if (isAnyModifierKeyButShift)
return NS_OK;
// else we insert the tab straight through
textEditor->HandleKeyPress(keyEvent);
// let HandleKeyPress consume the event
@@ -1027,16 +1029,40 @@ IsTargetFocused(nsIDOMEventTarget* aTarg
GetFocusedContent(getter_AddRefs(focusedContent));
// focusedContent will be null in the case where the document has focus,
// and so will content.
return (focusedContent == content);
}
+static nsIContent*
+FindEditableRoot(nsIContent *aContent)
+{
+ nsIDocument *document = aContent->GetCurrentDoc();
+ if (!document) {
+ return nsnull;
+ }
+
+ if (document->HasFlag(NODE_IS_EDITABLE)) {
+ return document->GetRootContent();
+ }
+
+ if (!aContent->HasFlag(NODE_IS_EDITABLE)) {
+ return nsnull;
+ }
+
+ nsIContent *parent, *content = aContent;
+ while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
+ content = parent;
+ }
+
+ return content;
+}
+
nsresult
nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
{
NS_ENSURE_ARG(aEvent);
// It's possible for us to receive a focus when we're really not focused.
// This happens, for example, when an onfocus handler that's hooked up
// before this listener focuses something else. In that case, all of the
// onblur handlers will be fired synchronously, then the remaining focus
@@ -1054,28 +1080,48 @@ nsTextEditorFocusListener::Focus(nsIDOME
if (mEditor)
{
aEvent->StopPropagation();
PRUint32 flags;
mEditor->GetFlags(&flags);
if (! (flags & nsIPlaintextEditor::eEditorDisabledMask))
{ // only enable caret and selection if the editor is not disabled
- nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
- if (editor)
+ nsCOMPtr<nsIContent> content = do_QueryInterface(target);
+
+ nsIContent *editableRoot = content ? FindEditableRoot(content) : nsnull;
+
+ nsCOMPtr<nsISelectionController> selCon;
+ mEditor->GetSelectionController(getter_AddRefs(selCon));
+ nsCOMPtr<nsIPresShell> presShell = do_QueryInterface(selCon);
+ if (selCon && editableRoot)
{
- nsCOMPtr<nsISelectionController>selCon;
- editor->GetSelectionController(getter_AddRefs(selCon));
- if (selCon)
+ nsCOMPtr<nsISelection> selection;
+ selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+ getter_AddRefs(selection));
+
+ if (presShell && selection) {
+ nsCOMPtr<nsICaret> caret;
+ presShell->GetCaret(getter_AddRefs(caret));
+ if (caret) {
+ caret->SetCaretDOMSelection(selection);
+ }
+ }
+
+ const PRBool kIsReadonly = (flags & nsIPlaintextEditor::eEditorReadonlyMask) != 0;
+ selCon->SetCaretReadOnly(kIsReadonly);
+ selCon->SetCaretEnabled(PR_TRUE);
+ selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+ selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
+
+ nsCOMPtr<nsISelectionPrivate> selectionPrivate =
+ do_QueryInterface(selection);
+ if (selectionPrivate)
{
- const PRBool kIsReadonly = (flags & nsIPlaintextEditor::eEditorReadonlyMask) != 0;
- selCon->SetCaretReadOnly(kIsReadonly);
- selCon->SetCaretEnabled(PR_TRUE);
- selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
- selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
+ selectionPrivate->SetAncestorLimiter(editableRoot);
}
}
}
nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor);
if (imeEditor)
imeEditor->NotifyIMEOnFocus();
}
@@ -1100,16 +1146,26 @@ nsTextEditorFocusListener::Blur(nsIDOMEv
nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
if (editor)
{
nsCOMPtr<nsISelectionController>selCon;
editor->GetSelectionController(getter_AddRefs(selCon));
if (selCon)
{
+ nsCOMPtr<nsISelection> selection;
+ selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+ getter_AddRefs(selection));
+
+ nsCOMPtr<nsISelectionPrivate> selectionPrivate =
+ do_QueryInterface(selection);
+ if (selectionPrivate) {
+ selectionPrivate->SetAncestorLimiter(nsnull);
+ }
+
selCon->SetCaretEnabled(PR_FALSE);
PRUint32 flags;
mEditor->GetFlags(&flags);
if((flags & nsIPlaintextEditor::eEditorWidgetMask) ||
(flags & nsIPlaintextEditor::eEditorPasswordMask) ||
(flags & nsIPlaintextEditor::eEditorReadonlyMask) ||
(flags & nsIPlaintextEditor::eEditorDisabledMask) ||
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -1291,17 +1291,19 @@ nsTextEditRules::CreateBogusNodeIfNeeded
// now we've got the body tag.
// iterate the body tag, looking for editable content
// if no editable content is found, insert the bogus node
PRBool needsBogusContent=PR_TRUE;
nsCOMPtr<nsIDOMNode> bodyChild;
nsresult res = mBody->GetFirstChild(getter_AddRefs(bodyChild));
while ((NS_SUCCEEDED(res)) && bodyChild)
{
- if (mEditor->IsMozEditorBogusNode(bodyChild) || mEditor->IsEditable(bodyChild))
+ if (mEditor->IsMozEditorBogusNode(bodyChild) ||
+ !mEditor->IsEditable(mBody) ||
+ mEditor->IsEditable(bodyChild))
{
needsBogusContent = PR_FALSE;
break;
}
nsCOMPtr<nsIDOMNode>temp;
bodyChild->GetNextSibling(getter_AddRefs(temp));
bodyChild = do_QueryInterface(temp);
}
--- a/editor/ui/composer/content/editor.js
+++ b/editor/ui/composer/content/editor.js
@@ -49,16 +49,17 @@ const kDisplayModeNormal = 0;
const kDisplayModeAllTags = 1;
const kDisplayModeSource = 2;
const kDisplayModePreview = 3;
const kDisplayModeMenuIDs = ["viewNormalMode", "viewAllTagsMode", "viewSourceMode", "viewPreviewMode"];
const kDisplayModeTabIDS = ["NormalModeButton", "TagModeButton", "SourceModeButton", "PreviewModeButton"];
const kNormalStyleSheet = "chrome://editor/content/EditorContent.css";
const kAllTagsStyleSheet = "chrome://editor/content/EditorAllTags.css";
const kParagraphMarksStyleSheet = "chrome://editor/content/EditorParagraphMarks.css";
+const kContentEditableStyleSheet = "resource:/res/contenteditable.css";
const kTextMimeType = "text/plain";
const kHTMLMimeType = "text/html";
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
var gPreviousNonSourceDisplayMode = 1;
var gEditorDisplayMode = -1;
@@ -394,16 +395,20 @@ var gEditorDocumentObserver =
if (!("InsertCharWindow" in window))
window.InsertCharWindow = null;
try {
editor.QueryInterface(nsIEditorStyleSheets);
// and extra styles for showing anchors, table borders, smileys, etc
editor.addOverrideStyleSheet(kNormalStyleSheet);
+
+ // remove contenteditable stylesheets if they were applied by the
+ // editingSession
+ editor.removeOverrideStyleSheet(kContentEditableStyleSheet);
} catch (e) {}
// Things for just the Web Composer application
if (IsWebComposer())
{
InlineSpellCheckerUI.init(editor);
document.getElementById('menu_inlinespellcheck').setAttribute('disabled', !InlineSpellCheckerUI.canSpellCheck);
--- a/embedding/browser/activex/src/control/MozillaBrowser.cpp
+++ b/embedding/browser/activex/src/control/MozillaBrowser.cpp
@@ -1286,17 +1286,18 @@ HRESULT CMozillaBrowser::SetEditorMode(B
if (!mEditingSession || !mCommandManager)
return E_FAIL;
nsCOMPtr<nsIDOMWindow> domWindow;
nsresult rv = GetDOMWindow(getter_AddRefs(domWindow));
if (NS_FAILED(rv))
return E_FAIL;
- rv = mEditingSession->MakeWindowEditable(domWindow, "html", PR_FALSE);
+ rv = mEditingSession->MakeWindowEditable(domWindow, "html", PR_FALSE,
+ PR_FALSE);
return S_OK;
}
HRESULT CMozillaBrowser::OnEditorCommand(DWORD nCmdID)
{
NG_TRACE_METHOD(CMozillaBrowser::OnEditorCommand);
--- a/embedding/config/basebrowser-installer-win.pkg
+++ b/embedding/config/basebrowser-installer-win.pkg
@@ -238,16 +238,18 @@ components\xmlextras.dll
; res:
;
res\html.css
res\quirk.css
res\viewsource.css
res\hiddenWindow.html
res\ua.css
res\forms.css
+res\contenteditable.css
+res\designmode.css
res\arrow.gif
res\arrowd.gif
res\loading-image.gif
res\broken-image.gif
res\entityTables\html40Special.properties
res\entityTables\htmlEntityVersions.properties
res\entityTables\html40Latin1.properties
res\entityTables\html40Symbols.properties
--- a/embedding/config/basebrowser-mac-macho
+++ b/embedding/config/basebrowser-mac-macho
@@ -233,16 +233,18 @@ chrome/embed.jar
; res:
;
res/html.css
res/quirk.css
res/viewsource.css
res/hiddenWindow.html
res/ua.css
res/forms.css
+res/contenteditable.css
+res/designmode.css
res/arrow.gif
res/arrowd.gif
res/loading-image.gif
res/broken-image.gif
res/entityTables/html40Special.properties
res/entityTables/htmlEntityVersions.properties
res/entityTables/html40Latin1.properties
res/entityTables/html40Symbols.properties
--- a/embedding/config/basebrowser-qnx
+++ b/embedding/config/basebrowser-qnx
@@ -233,16 +233,18 @@ chrome/embed.jar
; res:
;
res/html.css
res/quirk.css
res/viewsource.css
res/hiddenWindow.html
res/ua.css
res/forms.css
+res/contenteditable.css
+res/designmode.css
res/arrow.gif
res/arrowd.gif
res/loading-image.gif
res/broken-image.gif
res/entityTables/html40Special.properties
res/entityTables/htmlEntityVersion.properties
res/entityTables/html40Latin1.properties
res/entityTables/html40Symbols.properties
--- a/embedding/config/basebrowser-unix
+++ b/embedding/config/basebrowser-unix
@@ -235,16 +235,18 @@ chrome/embed.jar
; res:
;
res/html.css
res/quirk.css
res/viewsource.css
res/hiddenWindow.html
res/ua.css
res/forms.css
+res/contenteditable.css
+res/designmode.css
res/arrow.gif
res/arrowd.gif
res/loading-image.gif
res/broken-image.gif
res/entityTables/html40Special.properties
res/entityTables/htmlEntityVersion.properties
res/entityTables/html40Latin1.properties
res/entityTables/html40Symbols.properties
--- a/embedding/config/basebrowser-win
+++ b/embedding/config/basebrowser-win
@@ -254,16 +254,18 @@ components\xmlextras.dll
; res:
;
res\html.css
res\quirk.css
res\viewsource.css
res\hiddenWindow.html
res\ua.css
res\forms.css
+res/contenteditable.css
+res/designmode.css
res\arrow.gif
res\arrowd.gif
res\loading-image.gif
res\broken-image.gif
res\entityTables\html40Special.properties
res\entityTables\htmlEntityVersions.properties
res\entityTables\html40Latin1.properties
res\entityTables\html40Symbols.properties
--- a/embedding/config/minimo-qnx
+++ b/embedding/config/minimo-qnx
@@ -180,16 +180,18 @@ chrome/embed.jar
;
; res:
;
res/html.css
res/quirk.css
res/viewsource.css
res/ua.css
res/forms.css
+res/contenteditable.css
+res/designmode.css
res/arrow.gif
res/arrowd.gif
res/loading-image.gif
res/broken-image.gif
res/entityTables/html40Special.properties
res/entityTables/htmlEntityVersion.properties
res/entityTables/html40Latin1.properties
res/entityTables/html40Symbols.properties
--- a/embedding/config/minimo-unix
+++ b/embedding/config/minimo-unix
@@ -178,16 +178,18 @@ chrome/embed.jar
;
; res:
;
res/html.css
res/quirk.css
res/viewsource.css
res/ua.css
res/forms.css
+res/contenteditable.css
+res/designmode.css
res/arrow.gif
res/arrowd.gif
res/loading-image.gif
res/broken-image.gif
res/entityTables/html40Special.properties
res/entityTables/htmlEntityVersion.properties
res/entityTables/html40Latin1.properties
res/entityTables/html40Symbols.properties
--- a/embedding/qa/testembed/nsIEditSession.cpp
+++ b/embedding/qa/testembed/nsIEditSession.cpp
@@ -96,17 +96,18 @@ void CnsIEditSession::InitTest(PRInt16 d
QAOutput("Didn't get editingSession object for InitTest() test. Test failed.", displayMode);
}
void CnsIEditSession::MakeWinEditTest(PRBool afterUriLoad, PRInt16 displayMode)
{
editingSession = GetEditSessionObject();
domWindow = GetTheDOMWindow(qaWebBrowser);
if (editingSession) {
- rv= editingSession->MakeWindowEditable(domWindow, "text", afterUriLoad);
+ rv= editingSession->MakeWindowEditable(domWindow, "text", afterUriLoad,
+ PR_TRUE, PR_FALSE);
RvTestResult(rv, "MakeWindowEditable() test", displayMode);
if (displayMode == 1)
RvTestResultDlg(rv, "MakeWindowEditable() test");
if (!domWindow)
QAOutput("Didn't get domWindow object for MakeWindowEditable() test. Test failed.", displayMode);
}
else
QAOutput("Didn't get editingSession object for MakeWindowEditable() test. Test failed.", 1);
@@ -166,17 +167,17 @@ void CnsIEditSession::SetEditorWinTest(P
QAOutput("Didn't get object(s) for SetEditorWinTest() test. Test failed.", 1);
}
void CnsIEditSession::TearEditorWinTest(PRInt16 displayMode)
{
editingSession = GetEditSessionObject();
domWindow = GetTheDOMWindow(qaWebBrowser);
if (editingSession) {
- rv = editingSession->TearDownEditorOnWindow(domWindow);
+ rv = editingSession->TearDownEditorOnWindow(domWindow, PR_FALSE);
RvTestResult(rv, "TearDownEditorOnWindow() test", displayMode);
if (displayMode == 1)
RvTestResultDlg(rv, "TearDownEditorOnWindow() test");
if (!domWindow)
QAOutput("Didn't get domWindow object for TearDownEditorOnWindow() test. Test failed.", displayMode);
}
else
QAOutput("Didn't get object(s) for TearEditorWinTest() test. Test failed.", 1);
--- a/embedding/tests/wxEmbed/EditorFrame.cpp
+++ b/embedding/tests/wxEmbed/EditorFrame.cpp
@@ -79,17 +79,17 @@ EditorFrame::EditorFrame(wxWindow* aPare
void EditorFrame::MakeEditable()
{
nsCOMPtr<nsIDOMWindow> domWindow;
mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
nsCOMPtr<nsIEditingSession> editingSession = do_GetInterface(mWebBrowser);
if (!editingSession)
return;// NS_ERROR_FAILURE;
- editingSession->MakeWindowEditable(domWindow, NULL, PR_TRUE);
+ editingSession->MakeWindowEditable(domWindow, NULL, PR_TRUE, PR_FALSE);
}
nsresult EditorFrame::DoCommand(const char *aCommand, nsICommandParams *aCommandParams)
{
if (mCommandManager)
{
nsCOMPtr<nsIDOMWindow> domWindow;
mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -88,16 +88,18 @@
#include "nsISelection.h"
#include "nsISelection2.h"
#include "nsISelectionController.h"
#include "nsIServiceManager.h"
#include "nsITextServicesFilter.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsUnicharUtils.h"
+#include "nsIContent.h"
+#include "nsIEventStateManager.h"
// Set to spew messages to the console about what is happening.
//#define DEBUG_INLINESPELL
// the number of milliseconds that we will take at once to do spellchecking
#define INLINESPELL_CHECK_TIMEOUT 50
// The number of words to check before we look at the time to see if
@@ -1103,16 +1105,21 @@ mozInlineSpellChecker::SkipSpellCheckFor
*checkSpelling = PR_FALSE;
}
nsCOMPtr<nsIDOMNode> nextParent;
parent->GetParentNode(getter_AddRefs(nextParent));
parent = nextParent;
}
}
+ else {
+ // XXX Do we really want this for all read-write content?
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+ *checkSpelling = content->IntrinsicState() & NS_EVENT_STATE_MOZ_READWRITE;
+ }
return NS_OK;
}
// mozInlineSpellChecker::ScheduleSpellCheck
//
// This is called by code to do the actual spellchecking. We will set up
// the proper structures for calls to DoSpellCheck.
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -579,24 +579,20 @@ nsCaret::DrawAtPositionWithHint(nsIDOMNo
nsIFrame* theFrame = nsnull;
PRInt32 theFrameOffset = 0;
nsresult rv = GetCaretFrameForNodeOffset(contentNode, aOffset, aFrameHint, aBidiLevel,
&theFrame, &theFrameOffset);
if (NS_FAILED(rv) || !theFrame)
return PR_FALSE;
-
+
// now we have a frame, check whether it's appropriate to show the caret here
const nsStyleUserInterface* userinterface = theFrame->GetStyleUserInterface();
- if (
-#ifdef SUPPORT_USER_MODIFY
- // editable content still defaults to NS_STYLE_USER_MODIFY_READ_ONLY at present. See bug 15284
- (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
-#endif
+ if ((userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
(userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) ||
(userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED))
{
return PR_FALSE;
}
if (!mDrawn)
{
--- a/layout/build/layout.pkg
+++ b/layout/build/layout.pkg
@@ -30,16 +30,18 @@ dist/bin/components/@SHARED_LIBRARY@
#if MOZ_XUL
!xpt dist/bin/components/xultmpl.xpt
!xpt dist/bin/components/layout_xul_tree.xpt
#endif
dist/bin/res/html.css
dist/bin/res/quirk.css
dist/bin/res/viewsource.css
dist/bin/res/forms.css
+dist/bin/res/contenteditable.css
+dist/bin/res/designmode.css
dist/bin/res/arrow.gif
dist/bin/res/arrowd.gif
dist/bin/res/ua.css
dist/bin/res/broken-image.gif
dist/bin/res/loading-image.gif
dist/bin/res/html/gopher-audio.gif
dist/bin/res/html/gopher-binary.gif
dist/bin/res/html/gopher-find.gif
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5732,27 +5732,16 @@ nsIFrame::IsFocusable(PRInt32 *aTabIndex
}
PRBool isFocusable = PR_FALSE;
if (mContent && mContent->IsNodeOfType(nsINode::eELEMENT) &&
AreAncestorViewsVisible()) {
const nsStyleVisibility* vis = GetStyleVisibility();
if (vis->mVisible != NS_STYLE_VISIBILITY_COLLAPSE &&
vis->mVisible != NS_STYLE_VISIBILITY_HIDDEN) {
- if (mContent->IsNodeOfType(nsINode::eHTML)) {
- nsCOMPtr<nsISupports> container(PresContext()->GetContainer());
- nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(container));
- if (editorDocShell) {
- PRBool isEditable;
- editorDocShell->GetEditable(&isEditable);
- if (isEditable) {
- return NS_OK; // Editor content is not focusable
- }
- }
- }
const nsStyleUserInterface* ui = GetStyleUserInterface();
if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
// Pass in default tabindex of -1 for nonfocusable and 0 for focusable
tabIndex = 0;
}
isFocusable = mContent->IsFocusable(&tabIndex);
if (!isFocusable && !aWithMouse &&
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -452,16 +452,19 @@ public:
/** Get the content node that limits the selection
* When searching up a nodes for parents, as in a text edit field
* in an browser page, we must stop at this node else we reach into the
* parent page, which is very bad!
*/
nsIContent* GetLimiter() { return mLimiter; }
+ nsIContent* GetAncestorLimiter() { return mAncestorLimiter; }
+ void SetAncestorLimiter(nsIContent *aLimiter);
+
/** This will tell the frame selection that a double click has been pressed
* so it can track abort future drags if inside the same selection
* @aDoubleDown has the double click down happened
*/
void SetMouseDoubleDown(PRBool aDoubleDown) { mMouseDoubleDownState = aDoubleDown; }
/** This will return whether the double down flag was set.
* @return whether the double down flag was set
@@ -626,16 +629,18 @@ private:
// maintain selection
nsCOMPtr<nsIDOMRange> mMaintainRange;
nsSelectionAmount mMaintainedAmount;
//batching
PRInt32 mBatching;
nsIContent *mLimiter; //limit selection navigation to a child of this node.
+ nsIContent *mAncestorLimiter; // Limit selection navigation to a descendant of
+ // this node.
nsIPresShell *mShell;
PRInt16 mSelectionChangeReason; // reason for notifications of selection changing
PRInt16 mDisplaySelection; //for visual display purposes.
HINT mHint; //hint to tell if the selection is at the end of this line or beginning of next
#ifdef IBMBIDI
PRInt8 mCaretBidiLevel;
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -659,22 +659,32 @@ or composer)
*/
PRBool
IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsIContent *aContent)
{
if (!aFrameSel || !aContent)
return PR_FALSE;
if (aFrameSel)
{
- nsCOMPtr<nsIContent> tLimiter = aFrameSel->GetLimiter();
- if (tLimiter && tLimiter != aContent)
+ nsIContent *limiter = aFrameSel->GetLimiter();
+ if (limiter)
{
- if (tLimiter != aContent->GetParent()) //if newfocus == the limiter. that's ok. but if not there and not parent bad
+ if (limiter != aContent && limiter != aContent->GetParent()) //if newfocus == the limiter. that's ok. but if not there and not parent bad
return PR_FALSE; //not in the right content. tLimiter said so
}
+ limiter = aFrameSel->GetAncestorLimiter();
+ if (limiter)
+ {
+ nsIContent *content = aContent;
+ while (content && content != limiter)
+ {
+ content = content->GetParent();
+ }
+ return content != nsnull;
+ }
}
return PR_TRUE;
}
NS_IMPL_ADDREF(nsSelectionIterator)
NS_IMPL_RELEASE(nsSelectionIterator)
@@ -820,16 +830,17 @@ nsFrameSelection::nsFrameSelection()
return;
NS_ADDREF(mDomSelections[i]);
mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
}
mBatching = 0;
mChangesDuringBatching = PR_FALSE;
mNotifyFrames = PR_TRUE;
mLimiter = nsnull; //no default limiter.
+ mAncestorLimiter = nsnull;
mMouseDoubleDownState = PR_FALSE;
mHint = HINTLEFT;
#ifdef IBMBIDI
mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
#endif
mDragSelectingCells = PR_FALSE;
@@ -2215,18 +2226,20 @@ nsFrameSelection::HandleClick(nsIContent
PRBool aMultipleSelection,
PRBool aHint)
{
if (!aNewFocus)
return NS_ERROR_INVALID_ARG;
InvalidateDesiredX();
- if (!aContinueSelection)
+ if (!aContinueSelection) {
mMaintainRange = nsnull;
+ mAncestorLimiter = nsnull;
+ }
mHint = HINT(aHint);
// Don't take focus when dragging off of a table
if (!mDragSelectingCells)
{
BidiLevelFromClick(aNewFocus, aContentOffset);
PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
if (aContinueSelection &&
@@ -2781,28 +2794,31 @@ nsFrameSelection::IntraLineMove(PRBool a
nsresult
nsFrameSelection::SelectAll()
{
nsCOMPtr<nsIContent> rootContent;
if (mLimiter)
{
rootContent = mLimiter;//addrefit
}
+ else if (mAncestorLimiter) {
+ rootContent = mAncestorLimiter;
+ }
else
{
nsIDocument *doc = mShell->GetDocument();
if (!doc)
return NS_ERROR_FAILURE;
rootContent = doc->GetRootContent();
if (!rootContent)
return NS_ERROR_FAILURE;
}
PRInt32 numChildren = rootContent->GetChildCount();
PostReason(nsISelectionListener::NO_REASON);
- return TakeFocus(mLimiter, 0, numChildren, PR_FALSE, PR_FALSE);
+ return TakeFocus(rootContent, 0, numChildren, PR_FALSE, PR_FALSE);
}
//////////END FRAMESELECTION
void
nsFrameSelection::StartBatchChanges()
{
mBatching++;
@@ -3818,16 +3834,33 @@ nsFrameSelection::CreateAndAddRange(nsID
if (NS_FAILED(result)) return result;
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
return mDomSelections[index]->AddRange(range);
}
// End of Table Selection
+void
+nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
+{
+ if (mAncestorLimiter != aLimiter) {
+ mAncestorLimiter = aLimiter;
+ PRInt8 index =
+ GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!IsValidSelectionPoint(this, mDomSelections[index]->FetchFocusNode())) {
+ ClearNormalSelection();
+ if (mAncestorLimiter) {
+ PostReason(nsISelectionListener::NO_REASON);
+ TakeFocus(mAncestorLimiter, 0, 0, PR_FALSE, PR_FALSE);
+ }
+ }
+ }
+}
+
//END nsFrameSelection methods
#ifdef XP_MAC
#pragma mark -
#endif
//BEGIN nsISelection interface implementations
@@ -5286,16 +5319,23 @@ nsTypedSelection::GetCachedFrameOffset(n
NS_IMETHODIMP
nsTypedSelection::GetFrameSelection(nsFrameSelection **aFrameSelection) {
NS_ENSURE_ARG_POINTER(aFrameSelection);
*aFrameSelection = mFrameSelection;
NS_IF_ADDREF(*aFrameSelection);
return NS_OK;
}
+NS_IMETHODIMP
+nsTypedSelection::SetAncestorLimiter(nsIContent *aContent)
+{
+ mFrameSelection->SetAncestorLimiter(aContent);
+ return NS_OK;
+}
+
nsresult
nsTypedSelection::StartAutoScrollTimer(nsPresContext *aPresContext,
nsIView *aView,
nsPoint& aPoint,
PRUint32 aDelay)
{
NS_PRECONDITION(aView, "Need a view");
--- a/layout/style/Makefile.in
+++ b/layout/style/Makefile.in
@@ -173,16 +173,18 @@ LOCAL_INCLUDES = \
_FILES = \
ua.css \
html.css \
quirk.css \
viewsource.css \
arrow.gif \
arrowd.gif \
+ contenteditable.css \
+ designmode.css \
$(NULL)
GARBAGE += $(addprefix $(DIST)/bin/res/,$(_FILES))
FORMS_CSS_SRC = $(srcdir)/forms.css
PREPROCESS_FORMS_CSS = $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py --marker=% $(DEFINES) $(ACDEFINES) $(FORMS_CSS_SRC)
$(DIST)/bin/res/forms.css $(DESTDIR)$(mozappdir)/res/forms.css: $(FORMS_CSS_SRC) Makefile
new file mode 100644
--- /dev/null
+++ b/layout/style/contenteditable.css
@@ -0,0 +1,367 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Disruptive Innovations.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Peter Van der Beken <peterv@propagandism.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+::-moz-canvas {
+ cursor: text;
+}
+
+:focus:-moz-read-write :-moz-read-only {
+ -moz-user-select: all !important;
+}
+
+input:-moz-read-write > .anonymous-div:-moz-read-only,
+textarea:-moz-read-write > .anonymous-div:-moz-read-only {
+ -moz-user-select: text !important;
+}
+
+/* Use default arrow over objects with size that
+ are selected when clicked on.
+ Override the browser's pointer cursor over links
+*/
+
+img:-moz-read-write, img:-moz-read-write[usemap], area:-moz-read-write,
+object:-moz-read-write, object:-moz-read-write[usemap],
+applet:-moz-read-write, hr:-moz-read-write, button:-moz-read-write,
+isindex:-moz-read-write, select:-moz-read-write,
+a:link img, a:visited img, a:active img,
+a[name]:-moz-only-whitespace {
+ cursor: default;
+}
+
+:-moz-any-link:-moz-read-write {
+ cursor: text;
+ color : inherit;
+}
+
+/* Prevent clicking on links from going to link */
+a:link img, a:visited img {
+ -moz-user-input: none;
+}
+
+/* We suppress user/author's prefs for link underline,
+ so we must set explicitly. This isn't good!
+*/
+a:link:-moz-read-write {
+ text-decoration: underline -moz-anchor-decoration;
+ color: -moz-hyperlinktext;
+}
+
+/* Allow double-clicks on these widgets to open properties dialogs
+ XXX except when the widget has disabled attribute */
+:-moz-read-write > input:-moz-read-only,
+:-moz-read-write > button:-moz-read-only,
+:-moz-read-write > textarea:-moz-read-only {
+ -moz-user-select: all !important;
+ -moz-user-input: auto !important;
+ -moz-user-focus: none !important;
+}
+
+/* XXX Still need a better way of blocking other events to these widgets */
+select:-moz-read-write,
+:-moz-read-write > input[disabled],
+:-moz-read-write > input[type="checkbox"],
+:-moz-read-write > input[type="radio"],
+:-moz-read-write > input[type="file"],
+input[contenteditable="true"][disabled],
+input[contenteditable="true"][type="checkbox"],
+input[contenteditable="true"][type="radio"],
+input[contenteditable="true"][type="file"] {
+ -moz-user-select: all !important;
+ -moz-user-input: none !important;
+ -moz-user-focus: none !important;
+}
+
+isindex:-moz-read-write[prompt]
+{
+ -moz-user-select: none !important;
+ -moz-user-input: none !important;
+ -moz-user-focus: none !important;
+}
+
+:-moz-read-write > input[type="hidden"],
+input[contenteditable="true"][type="hidden"] {
+ border: 1px solid black !important;
+ visibility: visible !important;
+}
+
+label:-moz-read-write {
+ -moz-user-select: all !important;
+}
+
+::-moz-display-comboboxcontrol-frame {
+ -moz-user-select: text !important;
+}
+
+option:-moz-read-write {
+ -moz-user-select: text !important;
+}
+
+/* the following rules are for Image Resizing */
+
+span[\_moz_anonclass="mozResizer"] {
+ width: 5px;
+ height: 5px;
+ position: absolute;
+ border: 1px black solid;
+ background-color: white;
+ -moz-user-select: none;
+ z-index: 2147483646; /* max value -1 for this property */
+}
+
+/* we can't use :active below */
+span[\_moz_anonclass="mozResizer"][\_moz_activated],
+span[\_moz_anonclass="mozResizer"]:hover {
+ background-color: black;
+}
+
+span[\_moz_anonclass="mozResizer"].hidden,
+span[\_moz_anonclass="mozResizingShadow"].hidden,
+img[\_moz_anonclass="mozResizingShadow"].hidden,
+span[\_moz_anonclass="mozGrabber"].hidden,
+span[\_moz_anonclass="mozResizingInfo"].hidden,
+a[\_moz_anonclass="mozTableRemoveRow"].hidden,
+a[\_moz_anonclass="mozTableRemoveColumn"].hidden {
+ display: none !important;
+}
+
+span[\_moz_anonclass="mozResizer"][anonlocation="nw"] {
+ cursor: nw-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="n"] {
+ cursor: n-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="ne"] {
+ cursor: ne-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="w"] {
+ cursor: w-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="e"] {
+ cursor: e-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="sw"] {
+ cursor: sw-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="s"] {
+ cursor: s-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="se"] {
+ cursor: se-resize;
+}
+
+span[\_moz_anonclass="mozResizingShadow"],
+img[\_moz_anonclass="mozResizingShadow"] {
+ outline: thin dashed black;
+ -moz-user-select: none;
+ opacity: 0.5;
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+}
+
+span[\_moz_anonclass="mozResizingInfo"] {
+ font-family: sans-serif;
+ font-size: x-small;
+ color: black;
+ background-color: #d0d0d0;
+ border: ridge 2px #d0d0d0;
+ padding: 2px;
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+}
+
+img[\_moz_resizing] {
+ outline: thin solid black;
+}
+
+*[\_moz_abspos] {
+ outline: silver ridge 2px;
+ z-index: 2147483645 !important; /* max value -2 for this property */
+}
+*[\_moz_abspos="white"] {
+ background-color: white !important;
+}
+*[\_moz_abspos="black"] {
+ background-color: black !important;
+}
+
+span[\_moz_anonclass="mozGrabber"] {
+ outline: ridge 2px silver;
+ padding: 2px;
+ position: absolute;
+ width: 12px;
+ height: 12px;
+ background-image: url("resource:/res/grabber.gif");
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-user-select: none;
+ cursor: move;
+}
+
+/* INLINE TABLE EDITING */
+
+a[\_moz_anonclass="mozTableAddColumnBefore"] {
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+ text-decoration: none !important;
+ border: none 0px !important;
+ width: 4px;
+ height: 8px;
+ background-image: url("resource:/res/table-add-column-before.gif");
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-user-select: none !important;
+ -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddColumnBefore"]:hover {
+ background-image: url("resource:/res/table-add-column-before-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddColumnBefore"]:active {
+ background-image: url("resource:/res/table-add-column-before-active.gif");
+}
+
+a[\_moz_anonclass="mozTableAddColumnAfter"] {
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+ text-decoration: none !important;
+ border: none 0px !important;
+ width: 4px;
+ height: 8px;
+ background-image: url("resource:/res/table-add-column-after.gif");
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-user-select: none !important;
+ -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddColumnAfter"]:hover {
+ background-image: url("resource:/res/table-add-column-after-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddColumnAfter"]:active {
+ background-image: url("resource:/res/table-add-column-after-active.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveColumn"] {
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+ text-decoration: none !important;
+ border: none 0px !important;
+ width: 8px;
+ height: 8px;
+ background-image: url("resource:/res/table-remove-column.gif");
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-user-select: none !important;
+ -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableRemoveColumn"]:hover {
+ background-image: url("resource:/res/table-remove-column-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveColumn"]:active {
+ background-image: url("resource:/res/table-remove-column-active.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowBefore"] {
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+ text-decoration: none !important;
+ border: none 0px !important;
+ width: 8px;
+ height: 4px;
+ background-image: url("resource:/res/table-add-row-before.gif");
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-user-select: none !important;
+ -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddRowBefore"]:hover {
+ background-image: url("resource:/res/table-add-row-before-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowBefore"]:active {
+ background-image: url("resource:/res/table-add-row-before-active.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowAfter"] {
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+ text-decoration: none !important;
+ border: none 0px !important;
+ width: 8px;
+ height: 4px;
+ background-image: url("resource:/res/table-add-row-after.gif");
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-user-select: none !important;
+ -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddRowAfter"]:hover {
+ background-image: url("resource:/res/table-add-row-after-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowAfter"]:active {
+ background-image: url("resource:/res/table-add-row-after-active.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveRow"] {
+ position: absolute;
+ z-index: 2147483647; /* max value for this property */
+ text-decoration: none !important;
+ border: none 0px !important;
+ width: 8px;
+ height: 8px;
+ background-image: url("resource:/res/table-remove-row.gif");
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-user-select: none !important;
+ -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableRemoveRow"]:hover {
+ background-image: url("resource:/res/table-remove-row-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveRow"]:active {
+ background-image: url("resource:/res/table-remove-row-active.gif");
+}
new file mode 100644
--- /dev/null
+++ b/layout/style/designmode.css
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Disruptive Innovations.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Peter Van der Beken <peterv@propagandism.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+*|* {
+ -moz-user-modify: read-write;
+}
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -129,16 +129,21 @@ input > .anonymous-div {
padding: 0px 1px;
margin: 0px;
/* XXXldb I'm not sure if we really want the 'text-decoration: inherit',
but it's needed to make 'text-decoration' "work" on text inputs. */
text-decoration: inherit;
ime-mode: inherit;
}
+input:-moz-read-write,
+textarea:-moz-read-write {
+ -moz-user-modify: read-write !important;
+}
+
select {
margin: 0;
border-color: ThreeDFace;
background-color: -moz-Field;
color: -moz-FieldText;
font: -moz-list;
line-height: normal !important;
white-space: nowrap !important;
--- a/toolkit/content/widgets/editor.xml
+++ b/toolkit/content/widgets/editor.xml
@@ -61,17 +61,17 @@
})
]]>
</field>
<method name="makeEditable">
<parameter name="editortype"/>
<parameter name="waitForUrlLoad"/>
<body>
<![CDATA[
- this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad);
+ this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad, true, false);
this.setAttribute("editortype", editortype);
this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIURIContentListener)
.parentContentListener = this._editorContentListener;
]]>
</body>
</method>
--- a/xpfe/global/resources/content/bindings/editor.xml
+++ b/xpfe/global/resources/content/bindings/editor.xml
@@ -61,17 +61,17 @@
})
]]>
</field>
<method name="makeEditable">
<parameter name="editortype"/>
<parameter name="waitForUrlLoad"/>
<body>
<![CDATA[
- this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad);
+ this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad, true, false);
this.setAttribute("editortype", editortype);
this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIURIContentListener)
.parentContentListener = this._editorContentListener;
]]>
</body>
</method>
--- a/xpinstall/packager/packages-os2
+++ b/xpinstall/packager/packages-os2
@@ -370,16 +370,18 @@ bin/greprefs/*
bin/defaults/pref/*
bin/defaults/autoconfig/*
bin/res/hiddenWindow.html
bin/res/ua.css
bin/res/html.css
bin/res/quirk.css
bin/res/forms.css
bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
bin/res/grabber.gif
bin/res/table-add-column-after-active.gif
bin/res/table-add-column-after-hover.gif
bin/res/table-add-column-after.gif
bin/res/table-add-column-before-active.gif
bin/res/table-add-column-before-hover.gif
bin/res/table-add-column-before.gif
bin/res/table-add-row-after-active.gif
--- a/xpinstall/packager/packages-static-unix
+++ b/xpinstall/packager/packages-static-unix
@@ -252,16 +252,18 @@ bin/res/unixcharset.properties
bin/res/charsetData.properties
bin/res/langGroups.properties
bin/res/language.properties
bin/res/ua.css
bin/res/html.css
bin/res/quirk.css
bin/res/forms.css
bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
bin/res/grabber.gif
bin/res/table-add-column-after-active.gif
bin/res/table-add-column-after-hover.gif
bin/res/table-add-column-after.gif
bin/res/table-add-column-before-active.gif
bin/res/table-add-column-before-hover.gif
bin/res/table-add-column-before.gif
bin/res/table-add-row-after-active.gif
--- a/xpinstall/packager/packages-static-win
+++ b/xpinstall/packager/packages-static-win
@@ -253,16 +253,18 @@ bin\defaults\pref\*
bin\defaults\autoconfig\*
bin\res\hiddenWindow.html
bin\res\ua.css
bin\res\html.css
bin\res\quirk.css
bin\res\forms.css
bin\res\platform-forms.css
bin\res\EditorOverride.css
+bin\res\contenteditable.css
+bin\res\designmode.css
bin\res\viewsource.css
bin\res\mathml.css
bin\res\arrow.gif
bin\res\arrowd.gif
bin\res\loading-image.gif
bin\res\broken-image.gif
bin\res\html\*
bin\res\fonts\*
--- a/xpinstall/packager/packages-unix
+++ b/xpinstall/packager/packages-unix
@@ -388,16 +388,18 @@ bin/res/unixcharset.properties
bin/res/charsetData.properties
bin/res/langGroups.properties
bin/res/language.properties
bin/res/ua.css
bin/res/html.css
bin/res/quirk.css
bin/res/forms.css
bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
bin/res/grabber.gif
bin/res/table-add-column-after-active.gif
bin/res/table-add-column-after-hover.gif
bin/res/table-add-column-after.gif
bin/res/table-add-column-before-active.gif
bin/res/table-add-column-before-hover.gif
bin/res/table-add-column-before.gif
bin/res/table-add-row-after-active.gif