Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(294)

Side by Side Diff: ios/chrome/browser/voice/text_to_speech_parser.mm

Issue 2449593002: [ios] Adds support for parsing Text-to-Speech search results. (Closed)
Patch Set: Fix AVFoundation dependency. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/voice/text_to_speech_parser.h"
6
7 #include "base/logging.h"
8 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
9 #include "ios/web/public/web_state/web_state.h"
10 #import "third_party/google_toolbox_for_mac/src/Foundation/GTMStringEncoding.h"
11
12 namespace {
13
14 // The start and end tags that delimit TTS audio data.
15 NSString* const kTTSStartTag = @"function(){var _a_tts='";
16 NSString* const kTTSEndTag = @"'";
17
18 // When |kTTSAudioDataExtractorScriptFormat| is evaluated on a Google voice
19 // search results page, this script will extract the innerHTML from the script
20 // element containing TTS data. The format takes one parameter, which is the
21 // start tag from the TTS config singleton.
22 NSString* const kTTSAudioDataExtractorScriptFormat =
23 @"(function(){"
24 " var start_tag = \"%@\";"
25 " var script_elements = document.getElementsByTagName(\"script\");"
26 " for (var i = 0; i < script_elements.length; ++i) {"
27 " var script_html = script_elements[i].innerHTML;"
28 " if (script_html.indexOf(start_tag) > 0)"
29 " return script_html;"
30 " }"
31 " return \"\";"
32 "})()";
33 // Escaped encoding for GWS Voice Search serice's trailing '='.
sdefresne 2016/10/25 13:20:30 s/serice/service/?
rohitrao (ping after 24h) 2016/10/25 15:23:12 Done.
34 NSString* const kTrailingEqualEncoding = @"\\x3d";
35 // The maximum number of trailing '=' characters in a Voice Search SRP.
36 const NSUInteger kMaxTrailingEqualsCount = 2;
37 } // namespace
sdefresne 2016/10/25 13:20:30 nit: blank line before closing the namespace or re
rohitrao (ping after 24h) 2016/10/25 15:23:12 Done.
38
39 NSData* ExtractVoiceSearchAudioDataFromPageHTML(NSString* page_html) {
40 if (!page_html.length)
41 return nil;
42
43 // The data should be near the end of the page, so search backwards.
44 NSRange data_start_tag_range =
45 [page_html rangeOfString:kTTSStartTag options:NSBackwardsSearch];
46 if (data_start_tag_range.location == NSNotFound) {
47 DLOG(ERROR) << "Did not find base tts tag in search output. "
48 << page_html.length;
49 return nil;
50 }
51
52 // The base64-encoded data will be everything between
53 // |audioDataStartTag| and |audioDataEndTag|.
54 NSUInteger start_position =
55 data_start_tag_range.location + data_start_tag_range.length;
56 NSRange data_range =
57 NSMakeRange(start_position, page_html.length - start_position);
58 NSRange data_end_tag_range =
59 [page_html rangeOfString:kTTSEndTag options:0 range:data_range];
60 if (data_end_tag_range.location == NSNotFound ||
61 data_end_tag_range.location == start_position) {
62 DLOG(ERROR) << "Could not find encoded data before tts closing tag.";
63 return nil;
64 }
65
66 // Extract the data between the tags.
67 NSRange audio_data_range =
68 NSMakeRange(start_position, data_end_tag_range.location - start_position);
69 NSString* raw_base64_encoded_audio_string =
70 [page_html substringWithRange:audio_data_range];
71 if (!raw_base64_encoded_audio_string) {
72 DLOG(ERROR) << "Could not find encoded data between tags.";
73 return nil;
74 }
75
76 // GWS is escaping the trailing '=' characters to \x3d.
77 // Clean these up before passing the string to the base64 decoder.
78 // Note: there are at most 2 encoded trailing '=' characters, so limit the
79 // string replacement to the last characters of the string.
80 NSUInteger search_range_length =
81 std::min(kMaxTrailingEqualsCount * kTrailingEqualEncoding.length,
82 raw_base64_encoded_audio_string.length);
83 NSRange search_range =
84 NSMakeRange(raw_base64_encoded_audio_string.length - search_range_length,
85 search_range_length);
86 NSString* base64_encoded_audio_string = [raw_base64_encoded_audio_string
87 stringByReplacingOccurrencesOfString:kTrailingEqualEncoding
88 withString:@"="
89 options:0
90 range:search_range];
91
92 GTMStringEncoding* base64 = [GTMStringEncoding rfc4648Base64StringEncoding];
93 return [base64 decode:base64_encoded_audio_string];
94 }
95
96 void ExtractVoiceSearchAudioDataFromWebState(
97 web::WebState* webState,
98 TextToSpeechCompletion completion) {
99 DCHECK(webState);
100 DCHECK(completion);
101 NSString* tts_extraction_script = [NSString
102 stringWithFormat:kTTSAudioDataExtractorScriptFormat, kTTSStartTag];
103 [webState->GetJSInjectionReceiver()
104 executeJavaScript:tts_extraction_script
105 completionHandler:^(id result, NSError* error) {
106 completion(ExtractVoiceSearchAudioDataFromPageHTML(result));
107 }];
108 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698