avm1: Implement TextField.html

This commit is contained in:
Mike Welsh 2020-06-26 16:04:49 -07:00
parent d97a515330
commit b40f9d4c1a
3 changed files with 78 additions and 46 deletions

View File

@ -6,7 +6,6 @@ use crate::avm1::return_value::ReturnValue;
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
use crate::html::TextFormat;
use crate::xml::XMLDocument;
use gc_arena::MutationContext;
/// Implements `TextField`
@ -61,23 +60,23 @@ pub fn get_html<'gc>(
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() {
if let Some(_text_field) = display_object.as_edit_text() {
return Ok(true.into());
if let Some(text_field) = display_object.as_edit_text() {
return Ok(text_field.is_html().into());
}
}
Ok(Value::Undefined.into())
}
pub fn set_html<'gc>(
_avm: &mut Avm1<'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() {
if let Some(_text_field) = display_object.as_edit_text() {
if let Some(_value) = args.get(0) {
//TODO: Do something with this bool value
if let Some(text_field) = display_object.as_edit_text() {
if let Some(value) = args.get(0) {
text_field.set_is_html(context, value.as_bool(avm.current_swf_version()));
}
}
}
@ -92,17 +91,9 @@ pub fn get_html_text<'gc>(
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() {
if let Some(text_field) = display_object.as_edit_text() {
let html_tree = text_field.html_tree(context).as_node();
let html_string_result = html_tree.into_string(&mut |_node| true);
if let Err(err) = &html_string_result {
log::warn!(
"Serialization error when reading TextField.htmlText: {}",
err
);
if let Ok(text) = text_field.html_text(context) {
return Ok(text.into());
}
return Ok(html_string_result.unwrap_or_else(|_| "".to_string()).into());
}
}
Ok(Value::Undefined.into())
@ -115,26 +106,13 @@ pub fn set_html_text<'gc>(
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error<'gc>> {
if let Some(display_object) = this.as_display_object() {
if let Some(mut text_field) = display_object.as_edit_text() {
if let Some(value) = args.get(0) {
let html_string = value
.clone()
.coerce_to_string(avm, context)?
.into_owned()
.replace("<sbr>", "\n")
.replace("<br>", "\n");
let document = XMLDocument::new(context.gc_context);
if let Err(err) =
document
.as_node()
.replace_with_str(context.gc_context, &html_string, false)
{
log::warn!("Parsing error when setting TextField.htmlText: {}", err);
}
text_field.set_html_tree(document, context);
}
if let Some(text_field) = display_object.as_edit_text() {
let text = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_string(avm, context)?;
let _ = text_field.set_html_text(text.into_owned(), context);
// Changing the htmlText does NOT update variable bindings (does not call EditText::propagate_text_binding).
}
}
Ok(Value::Undefined.into())

View File

@ -187,7 +187,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
{
let _ = binding
.text_field
.set_text(value.coerce_to_string(avm, context)?.into_owned(), context);
.set_html_text(value.coerce_to_string(avm, context)?.into_owned(), context);
}
let result = if obj.base.has_own_property(avm, context, name) {

View File

@ -80,6 +80,9 @@ pub struct EditTextData<'gc> {
/// If the text field is required to use device fonts only.
is_device_font: bool,
/// If the text field renders as HTML.
is_html: bool,
/// The current border drawing.
drawing: Drawing,
@ -119,6 +122,7 @@ impl<'gc> EditText<'gc> {
) -> Self {
let is_multiline = swf_tag.is_multiline;
let is_word_wrap = swf_tag.is_word_wrap;
let is_html = swf_tag.is_html;
let document = XMLDocument::new(context.gc_context);
let text = swf_tag.initial_text.clone().unwrap_or_default();
let default_format = TextFormat::from_swf_tag(swf_tag.clone(), swf_movie.clone(), context);
@ -126,7 +130,7 @@ impl<'gc> EditText<'gc> {
let mut text_spans = FormatSpans::new();
text_spans.set_default_format(default_format.clone());
if swf_tag.is_html {
if is_html {
document
.as_node()
.replace_with_str(context.gc_context, &text, false)
@ -178,6 +182,7 @@ impl<'gc> EditText<'gc> {
is_word_wrap,
has_border,
is_device_font,
is_html,
drawing: Drawing::new(),
object: None,
layout,
@ -268,6 +273,51 @@ impl<'gc> EditText<'gc> {
Ok(())
}
pub fn html_text(self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<String, Error> {
if self.is_html() {
let html_tree = self.html_tree(context).as_node();
let html_string_result = html_tree.into_string(&mut |_node| true);
if let Err(err) = &html_string_result {
log::warn!(
"Serialization error when reading TextField.htmlText: {}",
err
);
}
return Ok(html_string_result.unwrap_or_else(|_| "".to_string()).into());
} else {
// Non-HTML text fields always return plain text.
return Ok(self.text());
}
}
pub fn set_html_text(
self,
text: String,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
if self.is_html() {
let html_string = text.replace("<sbr>", "\n").replace("<br>", "\n");
let document = XMLDocument::new(context.gc_context);
if let Err(err) =
document
.as_node()
.replace_with_str(context.gc_context, &html_string, false)
{
log::warn!("Parsing error when setting TextField.htmlText: {}", err);
}
self.set_html_tree(document, context);
} else {
if let Err(err) = self.set_text(text, context) {
log::error!("Error when setting TextField.htmlText: {}", err);
}
}
Ok(())
}
pub fn html_tree(self, context: &mut UpdateContext<'_, 'gc, '_>) -> XMLDocument<'gc> {
self.0.read().text_spans.raise_to_html(context.gc_context)
}
@ -282,11 +332,7 @@ impl<'gc> EditText<'gc> {
/// In stylesheet mode, the opposite is true: text spans are an
/// intermediate, user-facing text span APIs don't work, and the document
/// is retained.
pub fn set_html_tree(
&mut self,
doc: XMLDocument<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) {
pub fn set_html_tree(self, doc: XMLDocument<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) {
let mut write = self.0.write(context.gc_context);
write.document = doc;
@ -379,6 +425,14 @@ impl<'gc> EditText<'gc> {
self.relayout(context);
}
pub fn is_html(self) -> bool {
self.0.read().is_html
}
pub fn set_is_html(self, context: &mut UpdateContext<'_, 'gc, '_>, is_html: bool) {
self.0.write(context.gc_context).is_html = is_html;
}
pub fn replace_text(
self,
from: usize,
@ -713,7 +767,7 @@ impl<'gc> EditText<'gc> {
self.parent().unwrap(),
&variable_path,
) {
let text = if avm.current_swf_version() >= 6 {
let text = if self.0.read().is_html {
let html_tree = self.html_tree(context).as_node();
let html_string_result = html_tree.into_string(&mut |_node| true);
html_string_result.unwrap_or_default()