summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorbrg@chromium.com <brg@chromium.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-23 00:02:44 +0000
committerbrg@chromium.com <brg@chromium.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-23 00:02:44 +0000
commit1ebed56434c220f82cf220bbb72a136fea962c87 (patch)
tree7058028de7d017612d46ee49ba27b497c89cea7a /chrome
parent3c4d38386f27b5fc4a7a8db717ea114725c34b4a (diff)
downloadchromium_src-1ebed56434c220f82cf220bbb72a136fea962c87.zip
chromium_src-1ebed56434c220f82cf220bbb72a136fea962c87.tar.gz
chromium_src-1ebed56434c220f82cf220bbb72a136fea962c87.tar.bz2
Changes to insure that when in app-mode, links open in the default browser. This change should have no affect on Chrome when not in app-mode.Tested against Gmail. UITests added as app_mode_navigation_uitest.
Review URL: http://codereview.chromium.org/18093 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8523 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/app_mode_navigation_uitest.cc194
-rw-r--r--chrome/browser/browser.cc24
-rw-r--r--chrome/browser/browser.h12
-rw-r--r--chrome/browser/tab_contents/tab_contents_delegate.h14
-rw-r--r--chrome/browser/tab_contents/web_contents.cc49
-rw-r--r--chrome/browser/tab_contents/web_contents.h4
-rw-r--r--chrome/test/data/appmodenavigation_test.html21
-rw-r--r--chrome/test/ui/ui_tests.vcproj8
8 files changed, 300 insertions, 26 deletions
diff --git a/chrome/browser/app_mode_navigation_uitest.cc b/chrome/browser/app_mode_navigation_uitest.cc
new file mode 100644
index 0000000..e6175af
--- /dev/null
+++ b/chrome/browser/app_mode_navigation_uitest.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2006-2008 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.
+
+// Tests browsing in app-mode. Specifically, insures that navigation to
+// new windows launch in the default protocol handler and that navigations
+// within the same frame do not. Outside of app-mode these tests are covered
+// by normal navigation and the fork test.
+
+#include "base/string_util.h"
+#include "base/file_util.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/automation/window_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request_unittest.h"
+
+#include "chromium_strings.h"
+#include "generated_resources.h"
+
+namespace {
+
+const wchar_t kDocRoot[] = L"chrome/test/data";
+const wchar_t kTestFileName[] = L"appmodenavigation_test.html";
+
+class AppModeNavigationTest : public UITest {
+ protected:
+ AppModeNavigationTest() : UITest() {
+ show_window_ = true;
+
+ // Launch chrome in app mode.
+ std::wstring test_file = test_data_directory_;
+ file_util::AppendToPath(&test_file, kTestFileName);
+ std::wstring url = ASCIIToWide(net::FilePathToFileURL(test_file).spec());
+ launch_arguments_.AppendSwitchWithValue(switches::kApp, url);
+ }
+
+ // Helper function for evaluation.
+ HWND GetMainBrowserWindow() {
+ scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ scoped_ptr<WindowProxy> window(browser->GetWindow());
+
+ HWND window_handle;
+ EXPECT_TRUE(window->GetHWND(&window_handle));
+ return window_handle;
+ }
+
+ // Get the window title from the main window.
+ std::wstring GetMainBrowserWindowTitle() {
+ HWND window_handle = GetMainBrowserWindow();
+ std::wstring result;
+ int length = ::GetWindowTextLength(window_handle) + 1;
+ ::GetWindowText(window_handle, WriteInto(&result, length), length);
+ return result;
+ }
+
+ // Given a page title, returns the expected window caption string.
+ std::wstring WindowCaptionFromPageTitle(const std::wstring& page_title) {
+ if (page_title.empty())
+ return l10n_util::GetString(IDS_PRODUCT_NAME);
+ return l10n_util::GetStringF(IDS_BROWSER_WINDOW_TITLE_FORMAT, page_title);
+ }
+
+ // Selects the anchor tag by index and navigates to it. This is necssary
+ // since the automation API's navigate the window by simulating address bar
+ // entries.
+ void NavigateToIndex(int index) {
+ scoped_ptr<WindowProxy> window(automation()->GetActiveWindow());
+ ASSERT_TRUE(window->Activate());
+ Sleep(action_timeout_ms());
+ // We are in app mode, hence tab moves from focus to the next UI element
+ // in the view.
+ for (; index > 0; --index) {
+ window->SimulateOSKeyPress(L'\t', 0); // 0 signifies no modifier.
+ }
+ Sleep(action_timeout_ms());
+ window->SimulateOSKeyPress(L'\r', 0);
+ Sleep(action_timeout_ms());
+ }
+
+ // Validates that the foreground window is the external protocol handler
+ // validation window, and if so dismisses it by clicking on the default
+ // input.
+ bool DismissExternalLauncherPopup() {
+ // The currently active window should be the protocol handler if this is
+ // and external link.
+ scoped_ptr<WindowProxy> window(automation()->GetActiveWindow());
+ HWND hwnd;
+ if (!window->GetHWND(&hwnd))
+ return false;
+
+ int size = ::GetWindowTextLengthW(hwnd);
+ scoped_ptr<wchar_t> buffer(new wchar_t[size+1]);
+ if (NULL == buffer.get())
+ return false;
+ ::GetWindowText(hwnd, buffer.get(), size+1);
+ if (0 != wcscmp(L"External Protocol Request", buffer.get()))
+ return false;
+
+ // The default UI element is the |Cancel| button.
+ window->SimulateOSKeyPress(L'\r', 0);
+ Sleep(action_timeout_ms());
+
+ return true;
+ }
+};
+
+
+// Follow an normal anchor tag with target=blank (external tab).
+TEST_F(AppModeNavigationTest, NavigateToNewTab) {
+ scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ scoped_ptr<TabProxy> tab(browser->GetActiveTab());
+
+ int orig_tab_count = -1;
+ ASSERT_TRUE(browser->GetTabCount(&orig_tab_count));
+ int orig_process_count = GetBrowserProcessCount();
+ ASSERT_TRUE(orig_process_count >= 1);
+
+ NavigateToIndex(1); // First link test.
+
+ ASSERT_TRUE(DismissExternalLauncherPopup());
+
+ // Insure all the other processes have terminated.
+ ASSERT_EQ(orig_process_count, GetBrowserProcessCount());
+
+ // In app-mode there is a single tab.
+ int new_tab_count = -1;
+ ASSERT_TRUE(browser->GetTabCount(&new_tab_count));
+ ASSERT_EQ(orig_tab_count, new_tab_count);
+
+ // We must not have navigated.
+ EXPECT_EQ(WindowCaptionFromPageTitle(kTestFileName),
+ GetMainBrowserWindowTitle());
+ EXPECT_EQ(kTestFileName, GetActiveTabTitle());
+}
+
+// Open a new tab with a redirect as Gmail does.
+TEST_F(AppModeNavigationTest, NavigateByForkToNewTabTest) {
+ scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ scoped_ptr<TabProxy> tab(browser->GetActiveTab());
+
+ int orig_tab_count = -1;
+ ASSERT_TRUE(browser->GetTabCount(&orig_tab_count));
+ int orig_process_count = GetBrowserProcessCount();
+ ASSERT_TRUE(orig_process_count >= 1);
+
+ NavigateToIndex(2); // Second link test.
+
+ ASSERT_TRUE(DismissExternalLauncherPopup());
+
+ // Insure all the other processes have terminated.
+ ASSERT_EQ(orig_process_count, GetBrowserProcessCount());
+
+ // In app-mode there is a single tab.
+ int new_tab_count = -1;
+ ASSERT_TRUE(browser->GetTabCount(&new_tab_count));
+ ASSERT_EQ(orig_tab_count, new_tab_count);
+
+ // We must not have navigated.
+ EXPECT_EQ(WindowCaptionFromPageTitle(kTestFileName),
+ GetMainBrowserWindowTitle());
+ EXPECT_EQ(kTestFileName, GetActiveTabTitle());
+}
+
+// Normal navigation
+TEST_F(AppModeNavigationTest, NavigateToAboutBlankByLink) {
+ scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ scoped_ptr<TabProxy> tab(browser->GetActiveTab());
+
+ int orig_tab_count = -1;
+ ASSERT_TRUE(browser->GetTabCount(&orig_tab_count));
+ int orig_process_count = GetBrowserProcessCount();
+ ASSERT_TRUE(orig_process_count >= 1);
+
+ NavigateToIndex(3); // Third link test.
+
+ // Insure all the other processes have terminated.
+ ASSERT_EQ(orig_process_count, GetBrowserProcessCount());
+
+ // In app-mode there is a single tab.
+ int new_tab_count = -1;
+ ASSERT_TRUE(browser->GetTabCount(&new_tab_count));
+ ASSERT_EQ(orig_tab_count, new_tab_count);
+
+ // We must not have navigated.
+ EXPECT_EQ(WindowCaptionFromPageTitle(L"about:blank"),
+ GetMainBrowserWindowTitle());
+ EXPECT_EQ(L"", GetActiveTabTitle());
+}
+
+} // namespace
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 3cc13be..bdebf14 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -166,7 +166,8 @@ Browser::Browser(Type type, Profile* profile)
is_attempting_to_close_browser_(false),
override_maximized_(false),
method_factory_(this),
- idle_task_(new BrowserIdleTimer) {
+ idle_task_(new BrowserIdleTimer),
+ open_new_windows_in_default_browser_(false) {
tabstrip_model_.AddObserver(this);
NotificationService::current()->AddObserver(
@@ -1127,7 +1128,7 @@ void Browser::ExecuteCommand(int id) {
case IDC_NEW_WINDOW_PROFILE_5:
case IDC_NEW_WINDOW_PROFILE_6:
case IDC_NEW_WINDOW_PROFILE_7:
- case IDC_NEW_WINDOW_PROFILE_8:
+ case IDC_NEW_WINDOW_PROFILE_8:
NewProfileWindowByIndex(id - IDC_NEW_WINDOW_PROFILE_0); break;
#if defined(OS_WIN)
case IDC_CLOSE_WINDOW: CloseWindow(); break;
@@ -1675,12 +1676,14 @@ void Browser::AddNewContents(TabContents* source,
DCHECK(disposition != SAVE_TO_DISK); // No code for this yet
// If this is an application we can only have one tab so we need to process
- // this in tabbed browser window.
- if (tabstrip_model_.count() > 0 &&
+ // this in tabbed browser window. The new window will act as an intermediary
+ // to launch urls in the default browser.
+ if (tabstrip_model_.count() > 0 && // A launched application has count of 1.
disposition != NEW_WINDOW && disposition != NEW_POPUP &&
type_ != TYPE_NORMAL) {
Browser* b = GetOrCreateTabbedBrowser();
DCHECK(b);
+ b->set_open_new_windows_in_default_browser(true);
PageTransition::Type transition = PageTransition::LINK;
// If we were called from an "installed webapp" we want to emulate the code
// that is run from browser_init.cc for links from external applications.
@@ -1690,7 +1693,13 @@ void Browser::AddNewContents(TabContents* source,
if (type_ == TYPE_APP)
transition = PageTransition::START_PAGE;
b->tabstrip_model()->AddTabContents(new_contents, -1, transition, true);
- b->window()->Show();
+
+ // All new windows from an "installed webapp" which must have their own
+ // tabs will be launched in the default browser. At this moment the
+ // url is inaccessbile and so we do not show the window until the url is
+ // reported to the TabConentsDelegate.
+ if (type_ != TYPE_APP)
+ b->window()->Show();
return;
}
@@ -1800,6 +1809,10 @@ bool Browser::IsApplication() const {
return type_ == TYPE_APP;
}
+bool Browser::ShouldOpenURLInDefaultBrowser() const {
+ return open_new_windows_in_default_browser_;
+}
+
void Browser::ConvertContentsToApplication(TabContents* contents) {
WebContents* web_contents = contents->AsWebContents();
if (!web_contents || !web_contents->web_app()) {
@@ -2455,3 +2468,4 @@ void Browser::RegisterAppPrefs(const std::wstring& app_name) {
}
#endif // OS_WIN
+
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index 349b504..488d364 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -112,6 +112,10 @@ class Browser : public TabStripModelDelegate,
g_browser_process->user_data_dir_profiles() = profiles;
}
+ void set_open_new_windows_in_default_browser(bool value) {
+ open_new_windows_in_default_browser_ = value;
+ }
+
// Browser Creation Helpers /////////////////////////////////////////////////
// Opens a new window with the default blank tab.
@@ -393,11 +397,12 @@ class Browser : public TabStripModelDelegate,
virtual void ContentsZoomChange(bool zoom_in);
virtual bool IsApplication() const;
+ virtual bool ShouldOpenURLInDefaultBrowser() const;
virtual void ConvertContentsToApplication(TabContents* source);
virtual void ContentsStateChanged(TabContents* source);
virtual bool ShouldDisplayURLField();
virtual void BeforeUnloadFired(TabContents* source,
- bool proceed,
+ bool proceed,
bool* proceed_to_fire_unload);
virtual void ShowHtmlDialog(HtmlDialogContentsDelegate* delegate,
void* parent_window);
@@ -504,7 +509,7 @@ class Browser : public TabStripModelDelegate,
bool RemoveFromSet(UnloadListenerSet* set, TabContents* tab);
// Cleans up state appropriately when we are trying to close the browser and
- // the tab has finished firing it's unload handler. We also use this in the
+ // the tab has finished firing it's unload handler. We also use this in the
// cases where a tab crashes or hangs even if the beforeunload/unload haven't
// successfully fired.
void ClearUnloadState(TabContents* tab);
@@ -651,6 +656,9 @@ class Browser : public TabStripModelDelegate,
// Keep track of the encoding auto detect pref.
BooleanPrefMember encoding_auto_detect_;
+ // Allows a client of a TabDelegate to decide how to launch a url.
+ bool open_new_windows_in_default_browser_;
+
DISALLOW_COPY_AND_ASSIGN(Browser);
};
diff --git a/chrome/browser/tab_contents/tab_contents_delegate.h b/chrome/browser/tab_contents/tab_contents_delegate.h
index 599b64c..85458ba 100644
--- a/chrome/browser/tab_contents/tab_contents_delegate.h
+++ b/chrome/browser/tab_contents/tab_contents_delegate.h
@@ -30,8 +30,7 @@ class TabContentsDelegate : public PageNavigator {
virtual void OpenURL(const GURL& url, const GURL& referrer,
WindowOpenDisposition disposition,
- PageTransition::Type transition)
- {
+ PageTransition::Type transition) {
OpenURLFromTab(NULL, url, referrer, disposition, transition);
}
@@ -102,11 +101,14 @@ class TabContentsDelegate : public PageNavigator {
// application.
virtual bool IsApplication() { return false; }
+ // Check whether or not the url should be opened in the default browser.
+ virtual bool ShouldOpenURLInDefaultBrowser() const { return false; }
+
// Detach the given tab and convert it to a "webapp" view. The tab must be
// a WebContents with a valid WebApp set.
virtual void ConvertContentsToApplication(TabContents* source) { }
- // Informs the TabContentsDelegate that some of our state has changed
+ // Informs the TabContentsDelegate that some of our state has changed
// for this tab.
virtual void ContentsStateChanged(TabContents* source) {}
@@ -129,11 +131,11 @@ class TabContentsDelegate : public PageNavigator {
// Tells us that we've finished firing this tab's beforeunload event.
// The proceed bool tells us whether the user chose to proceed closing the
// tab. Returns true if the tab can continue on firing it's unload event.
- // If we're closing the entire browser, then we'll want to delay firing
+ // If we're closing the entire browser, then we'll want to delay firing
// unload events until all the beforeunload events have fired.
virtual void BeforeUnloadFired(TabContents* tab,
- bool proceed,
- bool* proceed_to_fire_unload) {
+ bool proceed,
+ bool* proceed_to_fire_unload) {
*proceed_to_fire_unload = true;
}
diff --git a/chrome/browser/tab_contents/web_contents.cc b/chrome/browser/tab_contents/web_contents.cc
index cf36173..05a026a 100644
--- a/chrome/browser/tab_contents/web_contents.cc
+++ b/chrome/browser/tab_contents/web_contents.cc
@@ -17,6 +17,7 @@
#include "chrome/browser/dom_operation_notification_details.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/download_request_manager.h"
+#include "chrome/browser/external_protocol_handler.h"
#include "chrome/browser/find_notification_details.h"
#include "chrome/browser/google_util.h"
#include "chrome/browser/js_before_unload_handler.h"
@@ -389,7 +390,7 @@ bool WebContents::NavigateToPendingEntry(bool reload) {
}
// Clear any provisional password saves - this stops password infobars
- // showing up on pages the user navigates to while the right page is
+ // showing up on pages the user navigates to while the right page is
// loading.
GetPasswordManager()->ClearProvisionalSave();
@@ -417,7 +418,7 @@ void WebContents::Copy() {
}
void WebContents::Paste() {
- render_view_host()->Paste();
+ render_view_host()->Paste();
}
void WebContents::DisassociateFromPopupCount() {
@@ -619,7 +620,7 @@ bool WebContents::IsActiveEntry(int32 page_id) {
}
void WebContents::SetInitialFocus(bool reverse) {
- render_view_host()->SetInitialFocus(reverse);
+ render_view_host()->SetInitialFocus(reverse);
}
// Notifies the RenderWidgetHost instance about the fact that the page is
@@ -684,22 +685,30 @@ void WebContents::DidNavigate(RenderViewHost* rvh,
if (PageTransition::IsMainFrame(params.transition))
render_manager_.DidNavigateMainFrame(rvh);
+ // This is the first chance a new frame has to launch an external browser
+ // since it is the first message from the renderer to the WebContents
+ // containing the URL.
+ if (delegate() && delegate()->ShouldOpenURLInDefaultBrowser()) {
+ OpenUrlInDefaultBrowserAndClosePage(params.url, rvh);
+ return;
+ }
+
// We can't do anything about navigations when we're inactive.
if (!controller() || !is_active())
- return;
+ return;
// Update the site of the SiteInstance if it doesn't have one yet.
if (!GetSiteInstance()->has_site())
GetSiteInstance()->SetSite(params.url);
- // Need to update MIME type here because it's referred to in
+ // Need to update MIME type here because it's referred to in
// UpdateNavigationCommands() called by RendererDidNavigate() to
- // determine whether or not to enable the encoding menu.
- // It's updated only for the main frame. For a subframe,
+ // determine whether or not to enable the encoding menu.
+ // It's updated only for the main frame. For a subframe,
// RenderView::UpdateURL does not set params.contents_mime_type.
// (see http://code.google.com/p/chromium/issues/detail?id=2929 )
- // TODO(jungshik): Add a test for the encoding menu to avoid
- // regressing it again.
+ // TODO(jungshik): Add a test for the encoding menu to avoid
+ // regressing it again.
if (PageTransition::IsMainFrame(params.transition))
contents_mime_type_ = params.contents_mime_type;
@@ -958,7 +967,12 @@ void WebContents::DidDownloadImage(
void WebContents::RequestOpenURL(const GURL& url, const GURL& referrer,
WindowOpenDisposition disposition) {
- OpenURL(url, referrer, disposition, PageTransition::LINK);
+ if (!delegate() || !delegate()->ShouldOpenURLInDefaultBrowser()) {
+ OpenURL(url, referrer, disposition, PageTransition::LINK);
+ } else {
+ OpenUrlInDefaultBrowserAndClosePage(url, render_view_host());
+ return;
+ }
}
void WebContents::DomOperationResponse(const std::string& json_string,
@@ -1073,7 +1087,7 @@ void WebContents::AutofillFormSubmitted(
GetAutofillManager()->AutofillFormSubmitted(form);
}
-void WebContents::GetAutofillSuggestions(const std::wstring& field_name,
+void WebContents::GetAutofillSuggestions(const std::wstring& field_name,
const std::wstring& user_text, int64 node_id, int request_id) {
GetAutofillManager()->FetchValuesForName(field_name, user_text,
kMaxAutofillMenuItems, node_id, request_id);
@@ -1288,7 +1302,7 @@ bool WebContents::CanBlur() const {
return delegate() ? delegate()->CanBlur() : true;
}
-void WebContents::RendererUnresponsive(RenderViewHost* rvh,
+void WebContents::RendererUnresponsive(RenderViewHost* rvh,
bool is_during_unload) {
if (is_during_unload) {
// Hang occurred while firing the beforeunload/unload handler.
@@ -1679,7 +1693,7 @@ bool WebContents::UpdateTitleForEntry(NavigationEntry* entry,
profile()->GetHistoryService(Profile::IMPLICIT_ACCESS);
if (hs)
hs->SetPageTitle(entry->display_url(), final_title);
-
+
// Don't allow the title to be saved again for explicitly set ones.
received_page_title_ = explicit_set;
}
@@ -1794,3 +1808,12 @@ void WebContents::GenerateKeywordIfNecessary(
new_url->set_safe_for_autoreplace(true);
url_model->Add(new_url);
}
+
+void WebContents::OpenUrlInDefaultBrowserAndClosePage(const GURL& url,
+ RenderViewHost* rvh) {
+ if (!url.is_empty() && rvh && rvh->process()) {
+ ExternalProtocolHandler::LaunchUrl(url, rvh->routing_id(),
+ rvh->process()->host_id());
+ }
+ ShouldClosePage(true);
+} \ No newline at end of file
diff --git a/chrome/browser/tab_contents/web_contents.h b/chrome/browser/tab_contents/web_contents.h
index c3094a4..c780fe9 100644
--- a/chrome/browser/tab_contents/web_contents.h
+++ b/chrome/browser/tab_contents/web_contents.h
@@ -480,6 +480,10 @@ class WebContents : public TabContents,
void GenerateKeywordIfNecessary(
const ViewHostMsg_FrameNavigate_Params& params);
+ // Helper function to launch the external browser.
+ void OpenUrlInDefaultBrowserAndClosePage(const GURL& url,
+ RenderViewHost* rvh);
+
// Data ----------------------------------------------------------------------
// The corresponding view.
diff --git a/chrome/test/data/appmodenavigation_test.html b/chrome/test/data/appmodenavigation_test.html
new file mode 100644
index 0000000..c938dc6
--- /dev/null
+++ b/chrome/test/data/appmodenavigation_test.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<Script Language="JavaScript">
+function load() {
+ w = window.open();
+ w.opener=null;
+ w.document.location='http://www.google.com';
+ w();
+}
+</Script>
+</head>
+<body>
+<a href="http://www.google.com" target=_blank> Link Test </a>
+<p>
+<div onclick='load()'>
+<a href=""> OnClick Test </a>
+</div>
+<p>
+<a href="about:blank"> Normal Test</a>
+</body>
+</html>
diff --git a/chrome/test/ui/ui_tests.vcproj b/chrome/test/ui/ui_tests.vcproj
index e225613..311bc95 100644
--- a/chrome/test/ui/ui_tests.vcproj
+++ b/chrome/test/ui/ui_tests.vcproj
@@ -542,6 +542,14 @@
>
</File>
</Filter>
+ <Filter
+ Name="TestAppModeNavigation"
+ >
+ <File
+ RelativePath="..\..\browser\app_mode_navigation_uitest.cc"
+ >
+ </File>
+ </Filter>
</Files>
<Globals>
</Globals>