1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_FRAME_UTILS_H_
#define CHROME_FRAME_UTILS_H_
#include <OAidl.h>
#include <objidl.h>
#include <windows.h>
#include <wininet.h>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/threading/thread.h"
#include "base/win/scoped_comptr.h"
#include "googleurl/src/gurl.h"
#include "ui/gfx/rect.h"
class FilePath;
interface IBrowserService;
interface IWebBrowser2;
struct ContextMenuModel;
// utils.h : Various utility functions and classes
extern const wchar_t kChromeContentPrefix[];
extern const char kGCFProtocol[];
extern const wchar_t kChromeProtocolPrefix[];
extern const wchar_t kChromeFrameHeadlessMode[];
extern const wchar_t kChromeFrameAccessibleMode[];
extern const wchar_t kChromeFrameUnpinnedMode[];
extern const wchar_t kAllowUnsafeURLs[];
extern const wchar_t kEnableBuggyBhoIntercept[];
extern const wchar_t kChromeMimeType[];
extern const wchar_t kChromeFrameAttachTabPattern[];
extern const wchar_t kChromeFrameConfigKey[];
extern const wchar_t kRenderInGCFUrlList[];
extern const wchar_t kRenderInHostUrlList[];
extern const wchar_t kEnableGCFRendererByDefault[];
extern const wchar_t kIexploreProfileName[];
extern const wchar_t kRundllProfileName[];
extern const wchar_t kUseBackgroundThreadForSubResources[];
// This function is very similar to the AtlRegisterTypeLib function except
// that it takes a parameter that specifies whether to register the typelib
// for the current user only or on a machine-wide basis
// Refer to the MSDN documentation for AtlRegisterTypeLib for a description of
// the arguments
HRESULT UtilRegisterTypeLib(HINSTANCE tlb_instance,
LPCOLESTR index,
bool for_current_user_only);
// This function is very similar to the AtlUnRegisterTypeLib function except
// that it takes a parameter that specifies whether to unregister the typelib
// for the current user only or on a machine-wide basis
// Refer to the MSDN documentation for AtlUnRegisterTypeLib for a description
// of the arguments
HRESULT UtilUnRegisterTypeLib(HINSTANCE tlb_instance,
LPCOLESTR index,
bool for_current_user_only);
HRESULT UtilRegisterTypeLib(LPCWSTR typelib_path, bool for_current_user_only);
HRESULT UtilUnRegisterTypeLib(LPCWSTR typelib_path, bool for_current_user_only);
HRESULT UtilRegisterTypeLib(ITypeLib* typelib,
LPCWSTR typelib_path,
LPCWSTR help_dir,
bool for_current_user_only);
HRESULT UtilUnRegisterTypeLib(ITypeLib* typelib,
bool for_current_user_only);
// Clears a marker that causes legacy NPAPI registration to persist across
// updates. Returns false if the marker could not be removed.
bool UtilRemovePersistentNPAPIMarker();
// Given an HTML fragment, this function looks for the
// <meta http-equiv="X-UA-Compatible"> tag and extracts the value of the
// "content" attribute
// This method will currently return a false positive if the tag appears
// inside a string in a <SCRIPT> block.
HRESULT UtilGetXUACompatContentValue(const std::wstring& html_string,
std::wstring* content_value);
// Returns a string from ChromeFrame's string table by resource. Must be
// provided with a valid resource id.
std::wstring GetResourceString(int resource_id);
// Displays a message box indicating that there was a version mismatch between
// ChromeFrame and the running instance of Chrome.
// server_version is the version of the running instance of Chrome.
void DisplayVersionMismatchWarning(HWND parent,
const std::string& server_version);
// This class provides a base implementation for ATL modules which want to
// perform all their registration under HKCU. This class overrides the
// RegisterServer and UnregisterServer methods and registers the type libraries
// under HKCU (the rest of the registration is made under HKCU by changing the
// appropriate .RGS files)
template < class BaseAtlModule >
class AtlPerUserModule : public BaseAtlModule {
public:
HRESULT RegisterServer(BOOL reg_typelib = FALSE,
const CLSID* clsid = NULL) throw() {
HRESULT hr = BaseAtlModule::RegisterServer(FALSE, clsid);
if (FAILED(hr)) {
return hr;
}
if (reg_typelib) {
hr = UtilRegisterTypeLib(_AtlComModule.m_hInstTypeLib, NULL, false);
}
return hr;
}
HRESULT UnregisterServer(BOOL unreg_typelib,
const CLSID* clsid = NULL) throw() {
HRESULT hr = BaseAtlModule::UnregisterServer(FALSE, clsid);
if (FAILED(hr)) {
return hr;
}
if (unreg_typelib) {
hr = UtilUnRegisterTypeLib(_AtlComModule.m_hInstTypeLib, NULL, false);
}
return hr;
}
};
// Creates a javascript statement for execution from the function name and
// arguments passed in.
std::string CreateJavascript(const std::string& function_name,
const std::string args);
// Use to prevent the DLL from being unloaded while there are still living
// objects with outstanding references.
class AddRefModule {
public:
AddRefModule();
~AddRefModule();
};
// Retrieves the executable name of the process hosting us. If
// |include_extension| is false, then we strip the extension from the name.
std::wstring GetHostProcessName(bool include_extension);
typedef enum BrowserType {
BROWSER_INVALID = -1,
BROWSER_UNKNOWN,
BROWSER_IE,
};
BrowserType GetBrowserType();
typedef enum IEVersion {
IE_INVALID,
NON_IE,
IE_UNSUPPORTED,
IE_6,
IE_7,
IE_8,
IE_9,
IE_10,
};
// The renderer to be used for a page. Values for Chrome also convey the
// reason why Chrome is used.
enum RendererType {
RENDERER_TYPE_UNDETERMINED = 0,
RENDERER_TYPE_CHROME_MIN,
// NOTE: group all _CHROME_ values together below here, as they are used for
// generating metrics reported via UMA (adjust MIN/MAX as needed).
RENDERER_TYPE_CHROME_GCF_PROTOCOL = RENDERER_TYPE_CHROME_MIN,
RENDERER_TYPE_CHROME_HTTP_EQUIV,
RENDERER_TYPE_CHROME_RESPONSE_HEADER,
RENDERER_TYPE_CHROME_DEFAULT_RENDERER,
RENDERER_TYPE_CHROME_OPT_IN_URL,
RENDERER_TYPE_CHROME_WIDGET,
// NOTE: all _CHOME_ values must go above here (adjust MIN/MAX as needed).
RENDERER_TYPE_CHROME_MAX = RENDERER_TYPE_CHROME_WIDGET,
RENDERER_TYPE_OTHER,
};
// Returns true if the given RendererType represents Chrome.
bool IsChrome(RendererType renderer_type);
// Convenience macro for logging a sample for the launch type metric.
#define UMA_LAUNCH_TYPE_COUNT(sample) \
UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.LaunchType", sample, \
RENDERER_TYPE_CHROME_MIN, RENDERER_TYPE_CHROME_MAX, \
RENDERER_TYPE_CHROME_MAX + 1 - RENDERER_TYPE_CHROME_MIN)
// To get the IE version when Chrome Frame is hosted in IE. Make sure that
// the hosting browser is IE before calling this function, otherwise NON_IE
// will be returned.
//
// Versions newer than the newest supported version are reported as the newest
// supported version.
IEVersion GetIEVersion();
// Returns the actual major version of the IE in which the current process is
// hosted. Returns 0 if the current process is not IE or any other error occurs.
uint32 GetIEMajorVersion();
FilePath GetIETemporaryFilesFolder();
// Retrieves the file version from a module handle without extra round trips
// to the disk (as happens with the regular GetFileVersionInfo API).
//
// @param module A handle to the module for which to retrieve the version info.
// @param high On successful return holds the most significant part of the file
// version. Must be non-null.
// @param low On successful return holds the least significant part of the file
// version. May be NULL.
// @returns true if the version info was successfully retrieved.
bool GetModuleVersion(HMODULE module, uint32* high, uint32* low);
// @returns the module handle to which an address belongs.
HMODULE GetModuleFromAddress(void* address);
// Return if the IEXPLORE is in private mode. The IEIsInPrivateBrowsing() checks
// whether current process is IEXPLORE.
bool IsIEInPrivate();
// Calls [ieframe|shdocvw]!DoFileDownload to initiate a download.
HRESULT DoFileDownloadInIE(const wchar_t* url);
// Construct a menu from the model sent from Chrome.
HMENU BuildContextMenu(const ContextMenuModel& menu_model);
// Uses GURL internally to append 'relative' to 'document'
std::string ResolveURL(const std::string& document,
const std::string& relative);
// Returns true iff the two urls have the same scheme, same host and same port.
bool HaveSameOrigin(const std::string& url1, const std::string& url2);
// Get a boolean configuration value from registry.
bool GetConfigBool(bool default_value, const wchar_t* value_name);
// Gets an integer configuration value from the registry.
int GetConfigInt(int default_value, const wchar_t* value_name);
// Sets an integer configuration value in the registry.
bool SetConfigInt(const wchar_t* value_name, int value);
// Sets a boolean integer configuration value in the registry.
bool SetConfigBool(const wchar_t* value_name, bool value);
// Deletes the configuration value passed in.
bool DeleteConfigValue(const wchar_t* value_name);
// Returns true if we are running in headless mode in which case we need to
// gather crash dumps, etc to send them to the crash server.
bool IsHeadlessMode();
// Returns true if we are running in accessible mode in which we need to enable
// renderer accessibility for use in automation.
bool IsAccessibleMode();
// Returns true if we are running in unpinned mode in which case DLL
// eviction should be possible.
bool IsUnpinnedMode();
// Returns true if all HTML pages should be rendered in GCF by default.
bool IsGcfDefaultRenderer();
// Check if this url is opting into Chrome Frame based on static settings.
// Returns one of:
// - RENDERER_TYPE_UNDETERMINED if not opt-in or if explicit opt-out
// - RENDERER_TYPE_CHROME_DEFAULT_RENDERER
// - RENDERER_TYPE_CHROME_OPT_IN_URL
RendererType RendererTypeForUrl(const std::wstring& url);
// A shortcut for QueryService
template <typename T>
HRESULT DoQueryService(const IID& service_id, IUnknown* unk, T** service) {
DCHECK(service);
if (!unk)
return E_INVALIDARG;
base::win::ScopedComPtr<IServiceProvider> service_provider;
HRESULT hr = service_provider.QueryFrom(unk);
if (service_provider)
hr = service_provider->QueryService(service_id, service);
DCHECK(FAILED(hr) || *service);
return hr;
}
// Navigates an IWebBrowser2 object to a moniker.
// |headers| can be NULL.
HRESULT NavigateBrowserToMoniker(IUnknown* browser, IMoniker* moniker,
const wchar_t* headers, IBindCtx* bind_ctx,
const wchar_t* fragment, IStream* post_data,
VARIANT* flags);
// Raises a flag on the current thread (using TLS) to indicate that an
// in-progress navigation should be rendered in chrome frame.
void MarkBrowserOnThreadForCFNavigation(IBrowserService* browser);
// Checks if this browser instance has been marked as currently navigating
// to a CF document. If clear_flag is set to true, the tls flag is cleared but
// only if the browser has been marked.
bool CheckForCFNavigation(IBrowserService* browser, bool clear_flag);
// Returns true if the URL passed in is something which can be handled by
// Chrome. If this function returns false then we should fail the navigation.
// When is_privileged is true, chrome extension URLs will be considered valid.
bool IsValidUrlScheme(const GURL& url, bool is_privileged);
// Returns the raw http headers for the current request given an
// IWinInetHttpInfo pointer.
std::string GetRawHttpHeaders(IWinInetHttpInfo* info);
// Can be used to determine whether a given request is being performed for
// a sub-frame or iframe in Internet Explorer. This can be called
// from various places, notably in request callbacks and the like.
//
// |service_provider| must not be NULL and should be a pointer to something
// that implements IServiceProvider (if it isn't this method returns false).
//
// Returns true if this method can determine with some certainty that the
// request did NOT originate from a top level frame, returns false otherwise.
bool IsSubFrameRequest(IUnknown* service_provider);
// See COM_INTERFACE_BLIND_DELEGATE below for details.
template <class T>
STDMETHODIMP CheckOutgoingInterface(void* obj, REFIID iid, void** ret,
DWORD cookie) {
T* instance = reinterpret_cast<T*>(obj);
HRESULT hr = E_NOINTERFACE;
IUnknown* delegate = instance ? instance->delegate() : NULL;
if (delegate) {
hr = delegate->QueryInterface(iid, ret);
#if !defined(NDEBUG)
if (SUCCEEDED(hr)) {
wchar_t iid_string[64] = {0};
StringFromGUID2(iid, iid_string, arraysize(iid_string));
DVLOG(1) << __FUNCTION__ << " Giving out wrapped interface: "
<< iid_string;
}
#endif
}
return hr;
}
// See COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS below for details.
template <class T>
STDMETHODIMP QueryInterfaceIfDelegateSupports(void* obj, REFIID iid,
void** ret, DWORD cookie) {
HRESULT hr = E_NOINTERFACE;
T* instance = reinterpret_cast<T*>(obj);
IUnknown* delegate = instance ? instance->delegate() : NULL;
if (delegate) {
base::win::ScopedComPtr<IUnknown> original;
hr = delegate->QueryInterface(iid,
reinterpret_cast<void**>(original.Receive()));
if (original) {
IUnknown* supported_interface = reinterpret_cast<IUnknown*>(
reinterpret_cast<DWORD_PTR>(obj) + cookie);
supported_interface->AddRef();
*ret = supported_interface;
hr = S_OK;
}
}
return hr;
}
// Same as COM_INTERFACE_ENTRY but relies on the class to implement a
// delegate() method that returns a pointer to the delegated COM object.
#define COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(x) \
COM_INTERFACE_ENTRY_FUNC(_ATL_IIDOF(x), \
offsetofclass(x, _ComMapClass), \
QueryInterfaceIfDelegateSupports<_ComMapClass>)
// Queries the delegated COM object for an interface, bypassing the wrapper.
#define COM_INTERFACE_BLIND_DELEGATE() \
COM_INTERFACE_ENTRY_FUNC_BLIND(0, CheckOutgoingInterface<_ComMapClass>)
// Thread that enters STA and has a UI message loop.
class STAThread : public base::Thread {
public:
explicit STAThread(const char *name) : Thread(name) {}
~STAThread() {
Stop();
}
bool Start() {
return StartWithOptions(Options(MessageLoop::TYPE_UI, 0));
}
protected:
// Called just prior to starting the message loop
virtual void Init() {
::CoInitialize(0);
}
// Called just after the message loop ends
virtual void CleanUp() {
::CoUninitialize();
}
};
std::wstring GuidToString(const GUID& guid);
// The urls retrieved from the IMoniker interface don't contain the anchor
// portion of the actual url navigated to. This function checks whether the
// url passed in the bho_url parameter contains an anchor and if yes checks
// whether it matches the url retrieved from the moniker. If yes it returns
// the bho url, if not the moniker url.
std::wstring GetActualUrlFromMoniker(IMoniker* moniker,
IBindCtx* bind_context,
const std::wstring& bho_url);
// Checks if a window is a top level window
bool IsTopLevelWindow(HWND window);
// Seeks a stream back to position 0.
HRESULT RewindStream(IStream* stream);
// Fired when we want to notify IE about privacy changes.
#define WM_FIRE_PRIVACY_CHANGE_NOTIFICATION (WM_APP + 1)
// Sent (not posted) when a request needs to be downloaded in the host browser
// instead of Chrome. WPARAM is 0 and LPARAM is a pointer to an IMoniker
// object.
// NOTE: Since the message is sent synchronously, the handler should only
// start asynchronous operations in order to not block the sender unnecessarily.
#define WM_DOWNLOAD_IN_HOST (WM_APP + 2)
// This structure contains the parameters sent over to initiate a download
// request in the host browser.
struct DownloadInHostParams {
base::win::ScopedComPtr<IBindCtx> bind_ctx;
base::win::ScopedComPtr<IMoniker> moniker;
base::win::ScopedComPtr<IStream> post_data;
std::string request_headers;
};
// Maps the InternetCookieState enum to the corresponding CookieAction values
// used for IE privacy stuff.
int32 MapCookieStateToCookieAction(InternetCookieState cookie_state);
// Parses the url passed in and returns a GURL instance without the fragment.
GURL GetUrlWithoutFragment(const wchar_t* url);
// Compares the URLs passed in after removing the fragments from them.
bool CompareUrlsWithoutFragment(const wchar_t* url1, const wchar_t* url2);
// Returns the Referrer from the HTTP headers and additional headers.
std::string FindReferrerFromHeaders(const wchar_t* headers,
const wchar_t* additional_headers);
// Returns the HTTP headers from the binding passed in.
std::string GetHttpHeadersFromBinding(IBinding* binding);
// Returns the HTTP response code from the binding passed in.
int GetHttpResponseStatusFromBinding(IBinding* binding);
// Returns the clipboard format for text/html.
CLIPFORMAT GetTextHtmlClipboardFormat();
// Returns true iff the mime type is text/html.
bool IsTextHtmlMimeType(const wchar_t* mime_type);
// Returns true iff the clipboard format is text/html.
bool IsTextHtmlClipFormat(CLIPFORMAT cf);
// Returns true if we can detect that we are running as SYSTEM, false otherwise.
bool IsSystemProcess();
// STL helper class that implements a functor to delete objects.
// E.g: std::for_each(v.begin(), v.end(), utils::DeleteObject());
namespace utils {
class DeleteObject {
public:
template <typename T>
void operator()(T* obj) {
delete obj;
}
};
}
// Convert various protocol flags to text representation. Used for logging.
std::string BindStatus2Str(ULONG bind_status);
std::string PiFlags2Str(DWORD flags);
std::string Bscf2Str(DWORD flags);
// Reads data from a stream into a string.
HRESULT ReadStream(IStream* stream, size_t size, std::string* data);
// Parses urls targeted at ChromeFrame. This class maintains state like
// whether a url is prefixed with the gcf: prefix, whether it is being
// attached to an existing external tab, etc.
class ChromeFrameUrl {
public:
ChromeFrameUrl();
// Parses the url passed in. Returns true on success.
bool Parse(const std::wstring& url);
bool is_chrome_protocol() const {
return is_chrome_protocol_;
}
bool attach_to_external_tab() const {
return attach_to_external_tab_;
}
uint64 cookie() const {
return cookie_;
}
int disposition() const {
return disposition_;
}
const gfx::Rect& dimensions() const {
return dimensions_;
}
const GURL& gurl() const {
return parsed_url_;
}
const std::string& profile_name() const {
return profile_name_;
}
private:
// If we are attaching to an existing external tab, this function parses the
// suffix portion of the URL which contains the attach_external_tab prefix.
bool ParseAttachExternalTabUrl();
// Clear state.
void Reset();
bool attach_to_external_tab_;
bool is_chrome_protocol_;
uint64 cookie_;
gfx::Rect dimensions_;
int disposition_;
GURL parsed_url_;
std::string profile_name_;
};
class NavigationConstraints;
// Returns true if we can navigate to this URL.
// These decisions are controlled by the NavigationConstraints object passed
// in.
bool CanNavigate(const GURL& url,
NavigationConstraints* navigation_constraints);
// Utility function that prevents the current module from ever being unloaded.
// Call if you make irreversible patches.
void PinModule();
// Helper function to spin a message loop and dispatch messages while waiting
// for a handle to be signaled.
void WaitWithMessageLoop(HANDLE* handles, int count, DWORD timeout);
// Enumerates values in a key and adds them to an array.
// The names of the values are not returned.
void EnumerateKeyValues(HKEY parent_key, const wchar_t* sub_key_name,
std::vector<std::wstring>* values);
// Interprets the value of an X-UA-Compatible header (or <meta> tag equivalent)
// and indicates whether the header value contains a Chrome Frame directive
// matching a given host browser version.
//
// The header is a series of name-value pairs, with the names being HTTP tokens
// and the values being either tokens or quoted-strings. Names and values are
// joined by '=' and pairs are delimited by either ';' or ','. LWS may be used
// liberally before and between names, values, '=', and ';' or ','. See RFC 2616
// for definitions of token, quoted-string, and LWS. See Microsoft's
// documentation of the X-UA-COMPATIBLE header here:
// http://msdn.microsoft.com/en-us/library/cc288325(VS.85).aspx
//
// At most one 'Chrome=<FILTER>' entry is expected in the header value. The
// first valid instance is used. The value of "<FILTER>" (possibly after
// unquoting) is interpreted as follows:
//
// "1" - Always active
// "IE7" - Active for IE major version 7 or lower
//
// For example:
// X-UA-Compatible: IE=8; Chrome=IE6
//
// The string is first interpreted using ';' as a delimiter. It is reevaluated
// using ',' iff no valid 'chrome=' value is found.
bool CheckXUaCompatibleDirective(const std::string& directive,
int ie_major_version);
// Returns the version of the current module as a string.
std::wstring GetCurrentModuleVersion();
// Returns true if ChromeFrame is the currently loaded document.
bool IsChromeFrameDocument(IWebBrowser2* web_browser);
// Increases the wininet connection limit for HTTP 1.0/1.1 connections to the
// value passed in. This is only done if the existing connection limit is
// lesser than the connection limit passed in. This function attempts to
// increase the connection count once per process.
// Returns true on success.
bool IncreaseWinInetConnections(DWORD connections);
#endif // CHROME_FRAME_UTILS_H_
|