Bug 145890

Summary: [OS X] Decoding fonts may return nil when using force click
Product: WebKit Reporter: Myles C. Maxfield <mmaxfield>
Component: New BugsAssignee: Myles C. Maxfield <mmaxfield>
Status: RESOLVED FIXED    
Severity: Normal CC: andersca, bdakin, commit-queue, darin, mitz, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Speculative fix
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch darin: review+

Description Myles C. Maxfield 2015-06-11 12:16:20 PDT
[OS X] Decoding fonts may return nil when using force click
Comment 1 Myles C. Maxfield 2015-06-11 12:17:35 PDT
Created attachment 254739 [details]
Speculative fix
Comment 2 Myles C. Maxfield 2015-06-11 12:18:20 PDT
<rdar://problem/21170487>
Comment 3 Myles C. Maxfield 2015-06-11 14:30:31 PDT
Committed r185475: <http://trac.webkit.org/changeset/185475>
Comment 4 Myles C. Maxfield 2015-06-11 18:36:28 PDT
This is not the best solution to this problem.
Comment 5 Myles C. Maxfield 2015-06-11 18:38:20 PDT
Created attachment 254773 [details]
Patch
Comment 6 Myles C. Maxfield 2015-06-11 19:14:34 PDT
Comment on attachment 254773 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=254773&action=review

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:345
> +            [dictionary setObject:value.get() forKey:key.get()];

Simon suggested that we may not even want to be in the business of claiming to transmit fonts in the first place (in favor of font attributes dictionaries). This would push this recreation failure logic into the user of IPC.
Comment 7 Myles C. Maxfield 2015-06-11 19:15:07 PDT
Comment on attachment 254773 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=254773&action=review

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:431
> +        if (value)

Transmitting an array with smaller size than the one sent seems incorrect.
Comment 8 Myles C. Maxfield 2015-06-11 19:38:36 PDT
Comment on attachment 254773 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=254773&action=review

>> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:345
>> +            [dictionary setObject:value.get() forKey:key.get()];
> 
> Simon suggested that we may not even want to be in the business of claiming to transmit fonts in the first place (in favor of font attributes dictionaries). This would push this recreation failure logic into the user of IPC.

This approach doesn't really work for attributed strings - forcing clients to recreate an attributed string from components seems needlessly complex. Perhaps instead I could have the attributed string encoding function inspect its argument for NSFonts upon ingestion.

Moving this to an encoding function rather than a decoding function seems cleaner. There may be a way to recognize a web font without trying a full encode / decode cycle.
Comment 9 Myles C. Maxfield 2015-06-11 19:46:12 PDT
Comment on attachment 254773 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=254773&action=review

>>> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:345
>>> +            [dictionary setObject:value.get() forKey:key.get()];
>> 
>> Simon suggested that we may not even want to be in the business of claiming to transmit fonts in the first place (in favor of font attributes dictionaries). This would push this recreation failure logic into the user of IPC.
> 
> This approach doesn't really work for attributed strings - forcing clients to recreate an attributed string from components seems needlessly complex. Perhaps instead I could have the attributed string encoding function inspect its argument for NSFonts upon ingestion.
> 
> Moving this to an encoding function rather than a decoding function seems cleaner. There may be a way to recognize a web font without trying a full encode / decode cycle.

Another alternative is to make the client of the IPC subsystem check for these non-serializable fonts, and then maintain an invariant that any font inside the IPC subsystem is serializable.
Comment 10 Darin Adler 2015-06-12 09:11:53 PDT
Comment on attachment 254773 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=254773&action=review

>>>> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:345
>>>> +            [dictionary setObject:value.get() forKey:key.get()];
>>> 
>>> Simon suggested that we may not even want to be in the business of claiming to transmit fonts in the first place (in favor of font attributes dictionaries). This would push this recreation failure logic into the user of IPC.
>> 
>> This approach doesn't really work for attributed strings - forcing clients to recreate an attributed string from components seems needlessly complex. Perhaps instead I could have the attributed string encoding function inspect its argument for NSFonts upon ingestion.
>> 
>> Moving this to an encoding function rather than a decoding function seems cleaner. There may be a way to recognize a web font without trying a full encode / decode cycle.
> 
> Another alternative is to make the client of the IPC subsystem check for these non-serializable fonts, and then maintain an invariant that any font inside the IPC subsystem is serializable.

Dictionary decoding seems too low a level to make a decision like this one, and changing the shape of the dictionary seems wrong to me. At this level the only behaviors that make sense to me are:

1) Failing entirely.
2) Turning an unsuccessfully transmitted value into some kind of object like an NSNull.

And I strongly prefer (1).

Generally speaking the encoders and decoders are too low a level to be dealing with this. Sure, the encoder could detect that this is a font that we can’t recreate by changing it into a font descriptor, and that seems better than having the decoder detect it, but there is no reasonable way for the encoder and decoder to handle that. The idea that we can send an attributed string across the process boundary with a web font as the font seems fundamentally flawed.

*** I think the primary problem here is that we are trying to change the code before we have decided what behavior we are trying to implement. What do we want the UI process to receive in the case of a string that is supposed to be using a font that exists only in the web process? ***

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:368
> -    return result;
> +    return true;

I’m not sure that you will want to roll this out once we make our final decision about behavior. I don’t know that “successfully” decoding a null NSFont is going to end up being a valuable part of the solution.

I don’t think that nil is a good way to represent “failed to transmit the NSFont successfully”. It seems more appropriate to use nil to encode something that was nil on the sending side, and some other way to indicate that we couldn’t transmit an NSFont across the process boundary.

>> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:431
>> +        if (value)
> 
> Transmitting an array with smaller size than the one sent seems incorrect.

I agree; array decoding is too low a level to make a decision like this one and changing the size of the array seems wrong to me. At this level the only behaviors that make sense to me are:

1) Failing entirely.
2) Turning an unsuccessfully transmitted value into some kind of object like an NSNull.

And I strongly prefer (1).
Comment 11 Darin Adler 2015-06-12 09:13:56 PDT
For example, do we want to substitute a different font? If so, which font?

Do we want to transmit the entire font to the UI process (seems bad for sandboxing)? Generally speaking I don’t think it’s safe for the UI process to try to render a web font.

I am pretty sure that this work should be done on the web process side in a separate step *before* trying to transmit the attributed string rather than as part of the encoding/decoding process.
Comment 12 Darin Adler 2015-06-12 09:14:46 PDT
I think while we try to figure that out we should make this fail cleanly and get that into the build, if the previous patch was insufficient for that.
Comment 13 Myles C. Maxfield 2015-06-15 14:57:10 PDT
<rdar://problem/21390877>
Comment 14 Myles C. Maxfield 2015-06-16 18:13:12 PDT
Created attachment 254988 [details]
Patch
Comment 15 Myles C. Maxfield 2015-06-16 18:15:09 PDT
Created attachment 254989 [details]
Patch
Comment 16 Myles C. Maxfield 2015-06-16 19:18:41 PDT
Comment on attachment 254989 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=254989&action=review

> Source/WebKit2/ChangeLog:5
> +        <rdar://problem/21170487>

Wrong radar number
Comment 17 Darin Adler 2015-06-16 19:27:05 PDT
Comment on attachment 254989 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=254989&action=review

> Source/WebKit2/WebProcess/WebPage/mac/WebPageMac.mm:579
> +            if (fontCanSurviveSerialization(font))
> +                [scaledAttributes setObject:font forKey:NSFontAttributeName];

This doesn’t remove the font from scaledAttributes if it can’t survive serialization. Need an else that calls removeObjectForKey: and you also need to do some testing to verify this does what you want it to do.
Comment 18 Myles C. Maxfield 2015-06-18 18:24:08 PDT
Created attachment 255160 [details]
Patch
Comment 19 Myles C. Maxfield 2015-06-18 19:28:35 PDT
Comment on attachment 255160 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=255160&action=review

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:215
> +    return [NSFontDescriptor fontDescriptorWithFontAttributes:[[font fontDescriptor] fontAttributes]];

Turns out this actually returns success if its run in the same process as the web font :(
Comment 20 Myles C. Maxfield 2015-06-18 19:52:04 PDT
Created attachment 255165 [details]
Patch
Comment 21 Myles C. Maxfield 2015-06-18 19:59:39 PDT
It looks like our lookup tests don't actually perform any IPC serialization
Comment 22 Darin Adler 2015-06-19 10:39:58 PDT
Comment on attachment 255165 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=255165&action=review

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:218
> +static inline bool fontIsSerializable(NSFont *font)
> +{
> +    return adoptCF(CTFontCopyAttribute((CTFontRef)font, kCTFontURLAttribute));
> +}

All three call sites that use this are using it the same way. They want a function that distinguishes non-serializable fonts from all other objects. Given the function name, filterUnserializableValues, below, I suggest adding this function:

    static inline bool isSerializableValue(id value)
    {
        return ![value isKindOfClass:[NSFont class]] || fontIsSerializable(value);
    }

You could use that in all three call sites.

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:222
> +    auto result = adoptNS([NSMutableDictionary new]);

We almost always use alloc/init instead of new; new is smaller code and slightly slower. I don’t think this function is a good one to be the first that does this!

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:223
> +    for (NSString *key in dictionary) {

I think it would be more efficient to do this with enumerateKeysAndObjectsUsingBlock: rather than a for loop.

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:224
> +        ASSERT([key isKindOfClass:[NSString class]]);

Doesn’t seem important to assert this.

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:225
> +        id object = dictionary[key];

This won’t compile on older platforms we still support. Needs to be:

    id object = [dictionary objectForKey:key];

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:226
> +        if (object && (![object isKindOfClass:[NSFont class]] || fontIsSerializable(object)))

Looks like this is compiling for iOS. To state the obvious, need to do an #if, since NSFont is not present on iOS. Or possibly use typedef to make the equivalent code work with UIFont?

No need to do if (object) here. That can be an assertion. NSDictionary doesn’t support having a key with an object of nil.
Comment 23 Darin Adler 2015-06-19 10:41:58 PDT
Comment on attachment 255165 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=255165&action=review

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:220
> +static inline RetainPtr<NSDictionary> filterUnserializableValues(NSDictionary *dictionary)

Kind of annoying that this always copies the entire dictionary even though the common case is that we don’t have to filter anything. Consider optimizing the case where we didn’t have to filter anything to discard the mutable dictionary and just return the passed-in dictionary? Or even use a two pass algorithm. The first pass checks for the normal case to make it faster. Or keep it simple since copying the dictionary is no big deal, I guess.
Comment 24 Myles C. Maxfield 2015-06-19 15:34:23 PDT
Comment on attachment 255165 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=255165&action=review

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:217
> +    return adoptCF(CTFontCopyAttribute((CTFontRef)font, kCTFontURLAttribute));

This is the wrong kind of cast
Comment 25 Myles C. Maxfield 2015-06-19 16:11:14 PDT
Created attachment 255243 [details]
Patch
Comment 26 WebKit Commit Bot 2015-06-19 16:13:38 PDT
Attachment 255243 [details] did not pass style-queue:


ERROR: Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:236:  Place brace on its own line for function definitions.  [whitespace/braces] [4]
ERROR: Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:246:  Place brace on its own line for function definitions.  [whitespace/braces] [4]
Total errors found: 2 in 2 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 27 Tim Horton 2015-06-19 16:17:26 PDT
Comment on attachment 255243 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=255243&action=review

> Source/WebKit2/ChangeLog:9
> +        Upon consulting with Anders, Thorton, and Beth, we realized that the best place to stop the serialization

no need for this naming of names

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:30
> +#if !USE(APPKIT)

PLATFORM(IOS)?

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:246
> +    [dictionary enumerateKeysAndObjectsUsingBlock:^(id __nonnull key, id __nonnull object, BOOL * __nonnull stop) {

Do you really need all these __nonnulls here?

> Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:248
> +            [result.get() setObject:object forKey:key];

No need for the .get()
Comment 28 Darin Adler 2015-06-19 16:29:48 PDT
Comment on attachment 255243 [details]
Patch

All of Tim’s comments make sense to me. Not sure why he didn’t say review+, though.
Comment 29 Myles C. Maxfield 2015-06-19 16:30:19 PDT
Created attachment 255245 [details]
Patch
Comment 30 WebKit Commit Bot 2015-06-19 16:32:04 PDT
Attachment 255245 [details] did not pass style-queue:


ERROR: Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:236:  Place brace on its own line for function definitions.  [whitespace/braces] [4]
ERROR: Source/WebKit2/Shared/mac/ArgumentCodersMac.mm:246:  Place brace on its own line for function definitions.  [whitespace/braces] [4]
Total errors found: 2 in 2 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 31 Myles C. Maxfield 2015-06-19 16:34:20 PDT
Committed r185775: <http://trac.webkit.org/changeset/185775>