<html>
    <head>
      <base href="https://bugs.webkit.org/" />
    </head>
    <body>
      <p>
        <div>
            <b><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - We should also store the time information for recent searches"
   href="https://bugs.webkit.org/show_bug.cgi?id=148388#c31">Comment # 31</a>
              on <a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - We should also store the time information for recent searches"
   href="https://bugs.webkit.org/show_bug.cgi?id=148388">bug 148388</a>
              from <span class="vcard"><a class="email" href="mailto:zacharyli323&#64;gmail.com" title="Zach Li &lt;zacharyli323&#64;gmail.com&gt;"> <span class="fn">Zach Li</span></a>
</span></b>
        <pre>(In reply to <a href="show_bug.cgi?id=148388#c29">comment #29</a>)
<span class="quote">&gt; (In reply to <a href="show_bug.cgi?id=148388#c28">comment #28</a>)
&gt; &gt; Comment on <span class=""><a href="attachment.cgi?id=262212&amp;action=diff" name="attach_262212" title="Patch v4">attachment 262212</a> <a href="attachment.cgi?id=262212&amp;action=edit" title="Patch v4">[details]</a></span>
&gt; &gt; Patch v4
&gt; &gt; 
&gt; &gt; View in context:
&gt; &gt; <a href="https://bugs.webkit.org/attachment.cgi?id=262212&amp;action=review">https://bugs.webkit.org/attachment.cgi?id=262212&amp;action=review</a>
&gt; &gt; 
&gt; &gt; Looks generally good, but review- because of the incorrect code to read from
&gt; &gt; disk (see below for details)
&gt; 
&gt; Yeah my code is not robust and I am making too many assumptions.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebCore/platform/SearchPopupMenu.h:40
&gt; &gt; &gt; +typedef struct RecentSearchItem {
&gt; &gt; &gt; +    RecentSearchItem() { }
&gt; &gt; &gt; +
&gt; &gt; &gt; +    RecentSearchItem(const String&amp; searchString, std::chrono::system_clock::time_point time)
&gt; &gt; &gt; +        : searchString(searchString), time(time) { }
&gt; &gt; &gt; +
&gt; &gt; &gt; +    String searchString;
&gt; &gt; &gt; +    std::chrono::system_clock::time_point time;
&gt; &gt; &gt; +} RecentSearchItem;
&gt; &gt; 
&gt; &gt; This typedef struct X {} X; pattern is completely unnecessary in C++. Just
&gt; &gt; struct X { } is fine. There is no need to create constructors for a struct
&gt; &gt; like this in modern C++. I also don’t think there’s a good reason to call
&gt; &gt; this searchString and time rather than searchString and searchTime or just
&gt; &gt; string and time. And further, there’s no reason to call this both recent
&gt; &gt; searches and recent search items. I don’t think the word items adds much. So
&gt; &gt; this should be:
&gt; &gt; 
&gt; &gt;     struct RecentSearch {
&gt; &gt;         String string;
&gt; &gt;         std::chrono::system_clock::time_point time;
&gt; &gt;     };
&gt; 
&gt; I was looking at examples of how struct is used in other places in WebKit
&gt; and a lot of them have constructors, so I thought it is a good practice, but
&gt; I will refine the struct declaration.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebCore/platform/win/SearchPopupMenuWin.cpp:94
&gt; &gt; &gt; +            searchItems.append(RecentSearchItem((String)(CFStringRef)item, std::chrono::system_clock::time_point::min()));
&gt; &gt; 
&gt; &gt; The cast to CFStringRef here is not helpful since item is already a
&gt; &gt; CFStringRef. The C-style cast to String here is not something we normally
&gt; &gt; do. I suggest a C++ style cast. Passing min() here requires a comment; it’s
&gt; &gt; not obvious why ti’s OK. And we should use aggregate syntax for this struct.
&gt; &gt; So please add a comment explaining why min is correct and write something
&gt; &gt; more like this:
&gt; &gt; 
&gt; &gt;     searchItems.append({ String{ item },
&gt; &gt; std::chrono::system_clock::time_point::min() });
&gt; 
&gt; I will add comments to explain why the use of min().
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebCore/rendering/RenderSearchField.cpp:94
&gt; &gt; &gt; +    m_recentSearches.insert(0, RecentSearchItem(value, std::chrono::system_clock::now()));
&gt; &gt; 
&gt; &gt; Please use aggregate syntax rather than constructor syntax:
&gt; &gt; 
&gt; &gt;     m_recentSearches.insert(0, { value, std::chrono::system_clock::now() });
&gt; 
&gt; Will do.
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:52
&gt; &gt; &gt; +static NSMutableDictionary *readSearchFieldRecentSearchesPlist()
&gt; &gt; &gt; +{
&gt; &gt; &gt; +    return [[NSMutableDictionary alloc] initWithContentsOfFile:searchFieldRecentSearchesPlistPath()];
&gt; &gt; &gt; +}
&gt; &gt; 
&gt; &gt; This function is dangerous. It returns a newly created Objective-C object,
&gt; &gt; but doesn’t use autorelease, nor wrap the pointer in a RetainPtr, nor does
&gt; &gt; it follow the create/copy rule for naming functions that create new objects
&gt; &gt; without autoreleasing them. I suggest using RetainPtr for the result here so
&gt; &gt; we don’t leak memory.
&gt; 
&gt; Nice catch. Will take care of the memory management.
&gt; 
&gt; &gt; 
&gt; &gt; I’m also not sure that [NSMutableDictionary initWithContentsOfFile:] is safe
&gt; &gt; to use on an untrusted file. We need to instead use an appropriate API that
&gt; &gt; doesn’t allow objects of arbitrary classes.</span >

Dan and I looked at the implementation of [NSMutableDictionary initWithContentsOfFile:], it will return nil when the object is not a dictionary. It also should return all the container classes in mutable form. But I will check if the dictionary is mutable before modifying it.

<span class="quote">&gt; 
&gt; When you say &quot;arbitrary classes&quot;, do you mean the plist should be a
&gt; dictionary, not an array or a string, etc.?
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:78
&gt; &gt; &gt; +    NSMutableDictionary *recentSearchesPlist = readSearchFieldRecentSearchesPlist();
&gt; &gt; 
&gt; &gt; Here we leak the contents of this file. That’s why we need to use a
&gt; &gt; RetainPtr or autorelease.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:80
&gt; &gt; &gt; +        [recentSearchesPlist[itemsKey] removeObjectForKey:name];
&gt; &gt; 
&gt; &gt; This won’t compile when targeting older versions of OS X, so we need to use
&gt; &gt; objectForKey here instead of array subscripting syntax.
&gt; &gt; 
&gt; &gt; This code assumes that type of the key in the property list that we read in
&gt; &gt; from will be a mutable dictionary and we can call removeObjectForKey on it.
&gt; &gt; We don’t have a guarantee of that. We need to check the type of the object
&gt; &gt; to see that it‘s a dictionary before modifying it, and we also need to make
&gt; &gt; a mutable copy of the dictionary unless we are using an API that guarantees
&gt; &gt; that any dictionaries it creates when reading the file are created mutable,
&gt; &gt; which we currently are not doing.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:84
&gt; &gt; &gt; +            NSTimeInterval timeInterval = duration_cast&lt;duration&lt;double&gt;&gt;(searchItem.time.time_since_epoch()).count();
&gt; &gt; 
&gt; &gt; I’d like to see a helper function template to convert a duration object into
&gt; &gt; an NSDate rather than writing this out here. It’s also not good that the
&gt; &gt; code that creates the interval since 1970 is on a different line than the
&gt; &gt; code that create the NSDate.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:88
&gt; &gt; &gt; +            NSDictionary *recentSearch = &#64;{
&gt; &gt; &gt; +                searchStringKey: searchItem.searchString,
&gt; &gt; &gt; +                dateKey: [NSDate dateWithTimeIntervalSince1970:timeInterval]
&gt; &gt; &gt; +            };
&gt; &gt; 
&gt; &gt; I’m pretty sure that the dictionary literal syntax is not supported in all
&gt; &gt; tools that we use for the versions of OS X that we currently target with
&gt; &gt; WebKit. This might need to use method calls instead. Unless I am mistaken.</span >

There are instances in WebKit where we use the dictionary literal syntax, but because
I had to use method calls in some places, I just used method calls instead of the
dictionary literal syntax in all the places.

<span class="quote">&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:94
&gt; &gt; &gt; +            [recentSearchesPlist[itemsKey] setObject:&#64;{ searchesKey: items.get() } forKey:name];
&gt; &gt; 
&gt; &gt; Same problem here as in the removeObjectForKey: call above, and same issue
&gt; &gt; with literals.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:96
&gt; &gt; &gt; +            recentSearchesPlist = [&#64;{ itemsKey: &#64;{ name: &#64;{ searchesKey: items.get() } } } mutableCopy];
&gt; &gt; 
&gt; &gt; Same issue with literals. Doesn’t seem to good to make a mutable copy here.
&gt; &gt; Wasteful.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:99
&gt; &gt; &gt; +    [recentSearchesPlist writeToFile:searchFieldRecentSearchesPlistPath() atomically:YES];
&gt; &gt; 
&gt; &gt; Using atomically:YES is costly in performance and in NAND life. We should be
&gt; &gt; sure this is necessary.</span >

I decided to use atomically:NO in the end since I added all these checks when reading the plist from the disk so even if the file is corrupted, it shouldn't be a big concern and we avoid the costly performance issue by using atomically:YES.

<span class="quote">&gt; 
&gt; I am setting it to YES because I am afraid the file might be corrupted while
&gt; writing. With my previous approach of reading the plist from disk, I assume
&gt; the file should be in the expected format. Your suggestions above will
&gt; alleviate the problem. But I still think we should write atomically, what do
&gt; you think?
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:112
&gt; &gt; &gt; +    NSArray *recentSearches = [[[recentSearchesPlist objectForKey:itemsKey] objectForKey:name] objectForKey:searchesKey];
&gt; &gt; 
&gt; &gt; Same comment about calling objectForKey: on an object of unknown type as
&gt; &gt; above.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:115
&gt; &gt; &gt; +            system_clock::time_point searchItemTime = system_clock::time_point(duration_cast&lt;system_clock::duration&gt;(duration&lt;double&gt;(dynamic_objc_cast&lt;NSDate&gt;(item[dateKey]).timeIntervalSince1970)));
&gt; &gt; 
&gt; &gt; Same comment about using helper functions to convert between NSDate and time
&gt; &gt; points as above.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:116
&gt; &gt; &gt; +            searchItems.append(WebCore::RecentSearchItem((String)item[searchStringKey], searchItemTime));
&gt; &gt; 
&gt; &gt; Same comment about aggregate syntax.
&gt; 
&gt; Will fix.
&gt; 
&gt; &gt; 
&gt; &gt; &gt; Source/WebKit2/UIProcess/Cocoa/WebPageProxyCocoa.mm:38
&gt; &gt; &gt; +static NSString * const dateKey = &#64;&quot;date&quot;;
&gt; &gt; &gt; +static NSString * const itemsKey = &#64;&quot;items&quot;;
&gt; &gt; &gt; +static NSString * const searchesKey = &#64;&quot;searches&quot;;
&gt; &gt; &gt; +static NSString * const searchStringKey = &#64;&quot;searchString&quot;;
&gt; &gt; 
&gt; &gt; Seems quite bad to literally have copied and pasted code for WebKit 1 and
&gt; &gt; WebKit 2. Please find a way to share more of the code.
&gt; 
&gt; Will do.
&gt; 
&gt; Thanks for the review!</span >

(In reply to <a href="show_bug.cgi?id=148388#c28">comment #28</a>)
<span class="quote">&gt; Comment on <span class=""><a href="attachment.cgi?id=262212&amp;action=diff" name="attach_262212" title="Patch v4">attachment 262212</a> <a href="attachment.cgi?id=262212&amp;action=edit" title="Patch v4">[details]</a></span>
&gt; Patch v4
&gt; 
&gt; View in context:
&gt; <a href="https://bugs.webkit.org/attachment.cgi?id=262212&amp;action=review">https://bugs.webkit.org/attachment.cgi?id=262212&amp;action=review</a>
&gt; 
&gt; Looks generally good, but review- because of the incorrect code to read from
&gt; disk (see below for details)
&gt; 
&gt; &gt; Source/WebCore/platform/SearchPopupMenu.h:40
&gt; &gt; +typedef struct RecentSearchItem {
&gt; &gt; +    RecentSearchItem() { }
&gt; &gt; +
&gt; &gt; +    RecentSearchItem(const String&amp; searchString, std::chrono::system_clock::time_point time)
&gt; &gt; +        : searchString(searchString), time(time) { }
&gt; &gt; +
&gt; &gt; +    String searchString;
&gt; &gt; +    std::chrono::system_clock::time_point time;
&gt; &gt; +} RecentSearchItem;
&gt; 
&gt; This typedef struct X {} X; pattern is completely unnecessary in C++. Just
&gt; struct X { } is fine. There is no need to create constructors for a struct
&gt; like this in modern C++. I also don’t think there’s a good reason to call
&gt; this searchString and time rather than searchString and searchTime or just
&gt; string and time. And further, there’s no reason to call this both recent
&gt; searches and recent search items. I don’t think the word items adds much. So
&gt; this should be:
&gt; 
&gt;     struct RecentSearch {
&gt;         String string;
&gt;         std::chrono::system_clock::time_point time;
&gt;     };
&gt; 
&gt; &gt; Source/WebCore/platform/win/SearchPopupMenuWin.cpp:94
&gt; &gt; +            searchItems.append(RecentSearchItem((String)(CFStringRef)item, std::chrono::system_clock::time_point::min()));
&gt; 
&gt; The cast to CFStringRef here is not helpful since item is already a
&gt; CFStringRef. The C-style cast to String here is not something we normally
&gt; do. I suggest a C++ style cast. Passing min() here requires a comment; it’s
&gt; not obvious why ti’s OK. And we should use aggregate syntax for this struct.
&gt; So please add a comment explaining why min is correct and write something
&gt; more like this:
&gt; 
&gt;     searchItems.append({ String{ item },
&gt; std::chrono::system_clock::time_point::min() });
&gt; 
&gt; &gt; Source/WebCore/rendering/RenderSearchField.cpp:94
&gt; &gt; +    m_recentSearches.insert(0, RecentSearchItem(value, std::chrono::system_clock::now()));
&gt; 
&gt; Please use aggregate syntax rather than constructor syntax:
&gt; 
&gt;     m_recentSearches.insert(0, { value, std::chrono::system_clock::now() });
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:52
&gt; &gt; +static NSMutableDictionary *readSearchFieldRecentSearchesPlist()
&gt; &gt; +{
&gt; &gt; +    return [[NSMutableDictionary alloc] initWithContentsOfFile:searchFieldRecentSearchesPlistPath()];
&gt; &gt; +}
&gt; 
&gt; This function is dangerous. It returns a newly created Objective-C object,
&gt; but doesn’t use autorelease, nor wrap the pointer in a RetainPtr, nor does
&gt; it follow the create/copy rule for naming functions that create new objects
&gt; without autoreleasing them. I suggest using RetainPtr for the result here so
&gt; we don’t leak memory.
&gt; 
&gt; I’m also not sure that [NSMutableDictionary initWithContentsOfFile:] is safe
&gt; to use on an untrusted file. We need to instead use an appropriate API that
&gt; doesn’t allow objects of arbitrary classes.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:78
&gt; &gt; +    NSMutableDictionary *recentSearchesPlist = readSearchFieldRecentSearchesPlist();
&gt; 
&gt; Here we leak the contents of this file. That’s why we need to use a
&gt; RetainPtr or autorelease.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:80
&gt; &gt; +        [recentSearchesPlist[itemsKey] removeObjectForKey:name];
&gt; 
&gt; This won’t compile when targeting older versions of OS X, so we need to use
&gt; objectForKey here instead of array subscripting syntax.
&gt; 
&gt; This code assumes that type of the key in the property list that we read in
&gt; from will be a mutable dictionary and we can call removeObjectForKey on it.
&gt; We don’t have a guarantee of that. We need to check the type of the object
&gt; to see that it‘s a dictionary before modifying it, and we also need to make
&gt; a mutable copy of the dictionary unless we are using an API that guarantees
&gt; that any dictionaries it creates when reading the file are created mutable,
&gt; which we currently are not doing.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:84
&gt; &gt; +            NSTimeInterval timeInterval = duration_cast&lt;duration&lt;double&gt;&gt;(searchItem.time.time_since_epoch()).count();
&gt; 
&gt; I’d like to see a helper function template to convert a duration object into
&gt; an NSDate rather than writing this out here. It’s also not good that the
&gt; code that creates the interval since 1970 is on a different line than the
&gt; code that create the NSDate.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:88
&gt; &gt; +            NSDictionary *recentSearch = &#64;{
&gt; &gt; +                searchStringKey: searchItem.searchString,
&gt; &gt; +                dateKey: [NSDate dateWithTimeIntervalSince1970:timeInterval]
&gt; &gt; +            };
&gt; 
&gt; I’m pretty sure that the dictionary literal syntax is not supported in all
&gt; tools that we use for the versions of OS X that we currently target with
&gt; WebKit. This might need to use method calls instead. Unless I am mistaken.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:94
&gt; &gt; +            [recentSearchesPlist[itemsKey] setObject:&#64;{ searchesKey: items.get() } forKey:name];
&gt; 
&gt; Same problem here as in the removeObjectForKey: call above, and same issue
&gt; with literals.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:96
&gt; &gt; +            recentSearchesPlist = [&#64;{ itemsKey: &#64;{ name: &#64;{ searchesKey: items.get() } } } mutableCopy];
&gt; 
&gt; Same issue with literals. Doesn’t seem to good to make a mutable copy here.
&gt; Wasteful.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:99
&gt; &gt; +    [recentSearchesPlist writeToFile:searchFieldRecentSearchesPlistPath() atomically:YES];
&gt; 
&gt; Using atomically:YES is costly in performance and in NAND life. We should be
&gt; sure this is necessary.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:112
&gt; &gt; +    NSArray *recentSearches = [[[recentSearchesPlist objectForKey:itemsKey] objectForKey:name] objectForKey:searchesKey];
&gt; 
&gt; Same comment about calling objectForKey: on an object of unknown type as
&gt; above.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:115
&gt; &gt; +            system_clock::time_point searchItemTime = system_clock::time_point(duration_cast&lt;system_clock::duration&gt;(duration&lt;double&gt;(dynamic_objc_cast&lt;NSDate&gt;(item[dateKey]).timeIntervalSince1970)));
&gt; 
&gt; Same comment about using helper functions to convert between NSDate and time
&gt; points as above.
&gt; 
&gt; &gt; Source/WebKit/mac/WebCoreSupport/SearchPopupMenuMac.mm:116
&gt; &gt; +            searchItems.append(WebCore::RecentSearchItem((String)item[searchStringKey], searchItemTime));
&gt; 
&gt; Same comment about aggregate syntax.
&gt; 
&gt; &gt; Source/WebKit2/UIProcess/Cocoa/WebPageProxyCocoa.mm:38
&gt; &gt; +static NSString * const dateKey = &#64;&quot;date&quot;;
&gt; &gt; +static NSString * const itemsKey = &#64;&quot;items&quot;;
&gt; &gt; +static NSString * const searchesKey = &#64;&quot;searches&quot;;
&gt; &gt; +static NSString * const searchStringKey = &#64;&quot;searchString&quot;;
&gt; 
&gt; Seems quite bad to literally have copied and pasted code for WebKit 1 and
&gt; WebKit 2. Please find a way to share more of the code.</span ></pre>
        </div>
      </p>
      <hr>
      <span>You are receiving this mail because:</span>
      
      <ul>
          <li>You are the assignee for the bug.</li>
      </ul>
    </body>
</html>