xref: /haiku/src/apps/webpositive/BrowserWindow.cpp (revision e9254dd79c44aaf90e68e5daa793e7f22b9b3636)
1 /*
2  * Copyright (C) 2007 Andrea Anzani <andrea.anzani@gmail.com>
3  * Copyright (C) 2007, 2010 Ryan Leavengood <leavengood@gmail.com>
4  * Copyright (C) 2009 Maxime Simon <simon.maxime@gmail.com>
5  * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
6  * Copyright (C) 2010 Michael Lotz <mmlr@mlotz.ch>
7  * Copyright (C) 2010 Rene Gollent <rene@gollent.com>
8  * Copyright 2013-2015 Haiku, Inc. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "BrowserWindow.h"
33 
34 #include <Alert.h>
35 #include <Application.h>
36 #include <Bitmap.h>
37 #include <Button.h>
38 #include <Catalog.h>
39 #include <CheckBox.h>
40 #include <Clipboard.h>
41 #include <ControlLook.h>
42 #include <Debug.h>
43 #include <Directory.h>
44 #include <Entry.h>
45 #include <File.h>
46 #include <FilePanel.h>
47 #include <FindDirectory.h>
48 #include <GridLayoutBuilder.h>
49 #include <GroupLayout.h>
50 #include <GroupLayoutBuilder.h>
51 #include <IconMenuItem.h>
52 #include <Keymap.h>
53 #include <LayoutBuilder.h>
54 #include <Locale.h>
55 #include <ObjectList.h>
56 #include <MenuBar.h>
57 #include <MenuItem.h>
58 #include <MessageRunner.h>
59 #include <NodeInfo.h>
60 #include <NodeMonitor.h>
61 #include <Path.h>
62 #include <Roster.h>
63 #include <Screen.h>
64 #include <SeparatorView.h>
65 #include <Size.h>
66 #include <SpaceLayoutItem.h>
67 #include <StatusBar.h>
68 #include <StringView.h>
69 #include <TextControl.h>
70 #include <UnicodeChar.h>
71 #include <Url.h>
72 
73 #include <map>
74 #include <stdio.h>
75 
76 #include "AuthenticationPanel.h"
77 #include "BaseURL.h"
78 #include "BitmapButton.h"
79 #include "BookmarkBar.h"
80 #include "BrowserApp.h"
81 #include "BrowsingHistory.h"
82 #include "CredentialsStorage.h"
83 #include "IconButton.h"
84 #include "NavMenu.h"
85 #include "SettingsKeys.h"
86 #include "SettingsMessage.h"
87 #include "TabManager.h"
88 #include "URLInputGroup.h"
89 #include "WebPage.h"
90 #include "WebView.h"
91 #include "WebViewConstants.h"
92 #include "WindowIcon.h"
93 
94 
95 #undef B_TRANSLATION_CONTEXT
96 #define B_TRANSLATION_CONTEXT "WebPositive Window"
97 
98 
99 enum {
100 	OPEN_LOCATION								= 'open',
101 	SAVE_PAGE									= 'save',
102 	GO_BACK										= 'goba',
103 	GO_FORWARD									= 'gofo',
104 	STOP										= 'stop',
105 	HOME										= 'home',
106 	GOTO_URL									= 'goul',
107 	RELOAD										= 'reld',
108 	SHOW_HIDE_BOOKMARK_BAR						= 'shbb',
109 	CLEAR_HISTORY								= 'clhs',
110 
111 	CREATE_BOOKMARK								= 'crbm',
112 	SHOW_BOOKMARKS								= 'shbm',
113 
114 	ZOOM_FACTOR_INCREASE						= 'zfin',
115 	ZOOM_FACTOR_DECREASE						= 'zfdc',
116 	ZOOM_FACTOR_RESET							= 'zfrs',
117 	ZOOM_TEXT_ONLY								= 'zfto',
118 
119 	TOGGLE_FULLSCREEN							= 'tgfs',
120 	TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN	= 'tgah',
121 	CHECK_AUTO_HIDE_INTERFACE					= 'cahi',
122 
123 	SHOW_PAGE_SOURCE							= 'spgs',
124 
125 	EDIT_SHOW_FIND_GROUP						= 'sfnd',
126 	EDIT_HIDE_FIND_GROUP						= 'hfnd',
127 	EDIT_FIND_NEXT								= 'fndn',
128 	EDIT_FIND_PREVIOUS							= 'fndp',
129 	FIND_TEXT_CHANGED							= 'ftxt',
130 
131 	SELECT_TAB									= 'sltb',
132 	CYCLE_TABS									= 'ctab',
133 };
134 
135 
136 static const int32 kModifiers = B_SHIFT_KEY | B_COMMAND_KEY
137 	| B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY;
138 
139 
140 static const char* kHandledProtocols[] = {
141 	"http",
142 	"https",
143 	"file",
144 	"about",
145 	"data",
146 	"gopher"
147 };
148 
149 static const char* kBookmarkBarSubdir = "Bookmark bar";
150 
151 static BLayoutItem*
layoutItemFor(BView * view)152 layoutItemFor(BView* view)
153 {
154 	BLayout* layout = view->Parent()->GetLayout();
155 	int32 index = layout->IndexOfView(view);
156 	return layout->ItemAt(index);
157 }
158 
159 
160 class BookmarkMenu : public BNavMenu {
161 public:
BookmarkMenu(const char * title,BHandler * target,const entry_ref * navDir)162 	BookmarkMenu(const char* title, BHandler* target, const entry_ref* navDir)
163 		:
164 		BNavMenu(title, B_REFS_RECEIVED, target)
165 	{
166 		// Add these items here already, so the shortcuts work even when
167 		// the menu has never been opened yet.
168 		_AddStaticItems();
169 
170 		SetNavDir(navDir);
171 	}
172 
AttachedToWindow()173 	virtual void AttachedToWindow()
174 	{
175 		RemoveItems(0, CountItems(), true);
176 		ForceRebuild();
177 		BNavMenu::AttachedToWindow();
178 		if (CountItems() > 0)
179 			AddItem(new BSeparatorItem(), 0);
180 		_AddStaticItems();
181 		DoLayout();
182 	}
183 
184 private:
_AddStaticItems()185 	void _AddStaticItems()
186 	{
187 		AddItem(new BMenuItem(B_TRANSLATE("Manage bookmarks"),
188 			new BMessage(SHOW_BOOKMARKS), 'M'), 0);
189 		AddItem(new BMenuItem(B_TRANSLATE("Bookmark this page"),
190 			new BMessage(CREATE_BOOKMARK), 'B'), 0);
191 	}
192 };
193 
194 
195 class PageUserData : public BWebView::UserData {
196 public:
PageUserData(BView * focusedView)197 	PageUserData(BView* focusedView)
198 		:
199 		fFocusedView(focusedView),
200 		fPageIcon(NULL),
201 		fURLInputSelectionStart(-1),
202 		fURLInputSelectionEnd(-1)
203 	{
204 	}
205 
~PageUserData()206 	~PageUserData()
207 	{
208 		delete fPageIcon;
209 	}
210 
SetFocusedView(BView * focusedView)211 	void SetFocusedView(BView* focusedView)
212 	{
213 		fFocusedView = focusedView;
214 	}
215 
FocusedView() const216 	BView* FocusedView() const
217 	{
218 		return fFocusedView;
219 	}
220 
SetPageIcon(const BBitmap * icon)221 	void SetPageIcon(const BBitmap* icon)
222 	{
223 		delete fPageIcon;
224 		if (icon)
225 			fPageIcon = new BBitmap(icon);
226 		else
227 			fPageIcon = NULL;
228 	}
229 
PageIcon() const230 	const BBitmap* PageIcon() const
231 	{
232 		return fPageIcon;
233 	}
234 
SetURLInputContents(const char * text)235 	void SetURLInputContents(const char* text)
236 	{
237 		fURLInputContents = text;
238 	}
239 
URLInputContents() const240 	const BString& URLInputContents() const
241 	{
242 		return fURLInputContents;
243 	}
244 
SetURLInputSelection(int32 selectionStart,int32 selectionEnd)245 	void SetURLInputSelection(int32 selectionStart, int32 selectionEnd)
246 	{
247 		fURLInputSelectionStart = selectionStart;
248 		fURLInputSelectionEnd = selectionEnd;
249 	}
250 
URLInputSelectionStart() const251 	int32 URLInputSelectionStart() const
252 	{
253 		return fURLInputSelectionStart;
254 	}
255 
URLInputSelectionEnd() const256 	int32 URLInputSelectionEnd() const
257 	{
258 		return fURLInputSelectionEnd;
259 	}
260 
261 private:
262 	BView*		fFocusedView;
263 	BBitmap*	fPageIcon;
264 	BString		fURLInputContents;
265 	int32		fURLInputSelectionStart;
266 	int32		fURLInputSelectionEnd;
267 };
268 
269 
270 class CloseButton : public BButton {
271 public:
CloseButton(BMessage * message)272 	CloseButton(BMessage* message)
273 		:
274 		BButton("close button", NULL, message),
275 		fOverCloseRect(false)
276 	{
277 		// Button is 16x16 regardless of font size
278 		SetExplicitMinSize(BSize(15, 15));
279 		SetExplicitMaxSize(BSize(15, 15));
280 	}
281 
Draw(BRect updateRect)282 	virtual void Draw(BRect updateRect)
283 	{
284 		BRect frame = Bounds();
285 		BRect closeRect(frame.InsetByCopy(4, 4));
286 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
287 		float tint = B_DARKEN_1_TINT;
288 
289 		if (fOverCloseRect)
290 			tint *= 1.4;
291 		else
292 			tint *= 1.2;
293 
294 		if (Value() == B_CONTROL_ON && fOverCloseRect) {
295 			// Draw the button frame
296 			be_control_look->DrawButtonFrame(this, frame, updateRect,
297 				base, base, BControlLook::B_ACTIVATED
298 					| BControlLook::B_BLEND_FRAME);
299 			be_control_look->DrawButtonBackground(this, frame,
300 				updateRect, base, BControlLook::B_ACTIVATED);
301 			closeRect.OffsetBy(1, 1);
302 			tint *= 1.2;
303 		} else {
304 			SetHighColor(base);
305 			FillRect(updateRect);
306 		}
307 
308 		// Draw the ×
309 		base = tint_color(base, tint);
310 		SetHighColor(base);
311 		SetPenSize(2);
312 		StrokeLine(closeRect.LeftTop(), closeRect.RightBottom());
313 		StrokeLine(closeRect.LeftBottom(), closeRect.RightTop());
314 		SetPenSize(1);
315 	}
316 
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)317 	virtual void MouseMoved(BPoint where, uint32 transit,
318 		const BMessage* dragMessage)
319 	{
320 		switch (transit) {
321 			case B_ENTERED_VIEW:
322 				fOverCloseRect = true;
323 				Invalidate();
324 				break;
325 			case B_EXITED_VIEW:
326 				fOverCloseRect = false;
327 				Invalidate();
328 				break;
329 			case B_INSIDE_VIEW:
330 				fOverCloseRect = true;
331 				break;
332 			case B_OUTSIDE_VIEW:
333 				fOverCloseRect = false;
334 				break;
335 		}
336 
337 		BButton::MouseMoved(where, transit, dragMessage);
338 	}
339 
340 private:
341 	bool fOverCloseRect;
342 };
343 
344 
345 // #pragma mark - BrowserWindow
346 
347 
BrowserWindow(BRect frame,SettingsMessage * appSettings,const BString & url,BPrivate::Network::BUrlContext * context,uint32 interfaceElements,BWebView * webView)348 BrowserWindow::BrowserWindow(BRect frame, SettingsMessage* appSettings,
349 		const BString& url, BPrivate::Network::BUrlContext* context,
350 		uint32 interfaceElements, BWebView* webView)
351 	:
352 	BWebWindow(frame, kApplicationName,
353 		B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
354 		B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS),
355 	fIsFullscreen(false),
356 	fInterfaceVisible(false),
357 	fMenusRunning(false),
358 	fPulseRunner(NULL),
359 	fVisibleInterfaceElements(interfaceElements),
360 	fContext(context),
361 	fAppSettings(appSettings),
362 	fZoomTextOnly(false),
363 	fShowTabsIfSinglePageOpen(true),
364 	fAutoHideInterfaceInFullscreenMode(false),
365 	fAutoHidePointer(false),
366 	fBookmarkBar(NULL)
367 {
368 	// Begin listening to settings changes and read some current values.
369 	fAppSettings->AddListener(BMessenger(this));
370 	fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly);
371 	fShowTabsIfSinglePageOpen = fAppSettings->GetValue(
372 		kSettingsKeyShowTabsIfSinglePageOpen, fShowTabsIfSinglePageOpen);
373 
374 	fAutoHidePointer = fAppSettings->GetValue(kSettingsKeyAutoHidePointer,
375 		fAutoHidePointer);
376 
377 	fNewWindowPolicy = fAppSettings->GetValue(kSettingsKeyNewWindowPolicy,
378 		(uint32)OpenStartPage);
379 	fNewTabPolicy = fAppSettings->GetValue(kSettingsKeyNewTabPolicy,
380 		(uint32)OpenBlankPage);
381 	fStartPageURL = fAppSettings->GetValue(kSettingsKeyStartPageURL,
382 		kDefaultStartPageURL);
383 	fSearchPageURL = fAppSettings->GetValue(kSettingsKeySearchPageURL,
384 		kDefaultSearchPageURL);
385 
386 	// Create the interface elements
387 	BMessage* newTabMessage = new BMessage(NEW_TAB);
388 	newTabMessage->AddString("url", "");
389 	newTabMessage->AddPointer("window", this);
390 	newTabMessage->AddBool("select", true);
391 	fTabManager = new TabManager(BMessenger(this), newTabMessage);
392 
393 	// Menu
394 #if INTEGRATE_MENU_INTO_TAB_BAR
395 	BMenu* mainMenu = new BMenu("≡");
396 #else
397 	BMenu* mainMenu = new BMenuBar("Main menu");
398 #endif
399 	BMenu* menu = new BMenu(B_TRANSLATE("Window"));
400 	BMessage* newWindowMessage = new BMessage(NEW_WINDOW);
401 	newWindowMessage->AddString("url", "");
402 	BMenuItem* newItem = new BMenuItem(B_TRANSLATE("New window"),
403 		newWindowMessage, 'N');
404 	menu->AddItem(newItem);
405 	newItem->SetTarget(be_app);
406 	newItem = new BMenuItem(B_TRANSLATE("New tab"),
407 		new BMessage(*newTabMessage), 'T');
408 	menu->AddItem(newItem);
409 	newItem->SetTarget(be_app);
410 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open location"),
411 		new BMessage(OPEN_LOCATION), 'L'));
412 	menu->AddSeparatorItem();
413 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close window"),
414 		new BMessage(B_QUIT_REQUESTED), 'W', B_SHIFT_KEY));
415 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close tab"),
416 		new BMessage(CLOSE_TAB), 'W'));
417 	menu->AddItem(new BMenuItem(B_TRANSLATE("Save page as" B_UTF8_ELLIPSIS),
418 		new BMessage(SAVE_PAGE), 'S'));
419 	menu->AddSeparatorItem();
420 	menu->AddItem(new BMenuItem(B_TRANSLATE("Downloads"),
421 		new BMessage(SHOW_DOWNLOAD_WINDOW), 'D'));
422 	menu->AddItem(new BMenuItem(B_TRANSLATE("Settings"),
423 		new BMessage(SHOW_SETTINGS_WINDOW), ','));
424 	menu->AddItem(new BMenuItem(B_TRANSLATE("Cookie manager"),
425 		new BMessage(SHOW_COOKIE_WINDOW)));
426 	menu->AddItem(new BMenuItem(B_TRANSLATE("Script console"),
427 		new BMessage(SHOW_CONSOLE_WINDOW)));
428 	BMenuItem* aboutItem = new BMenuItem(B_TRANSLATE("About"),
429 		new BMessage(B_ABOUT_REQUESTED));
430 	menu->AddItem(aboutItem);
431 	aboutItem->SetTarget(be_app);
432 	menu->AddSeparatorItem();
433 	BMenuItem* quitItem = new BMenuItem(B_TRANSLATE("Quit"),
434 		new BMessage(B_QUIT_REQUESTED), 'Q');
435 	menu->AddItem(quitItem);
436 	quitItem->SetTarget(be_app);
437 	mainMenu->AddItem(menu);
438 
439 	menu = new BMenu(B_TRANSLATE("Edit"));
440 	menu->AddItem(fCutMenuItem = new BMenuItem(B_TRANSLATE("Cut"),
441 		new BMessage(B_CUT), 'X'));
442 	menu->AddItem(fCopyMenuItem = new BMenuItem(B_TRANSLATE("Copy"),
443 		new BMessage(B_COPY), 'C'));
444 	menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"),
445 		new BMessage(B_PASTE), 'V'));
446 	menu->AddSeparatorItem();
447 	menu->AddItem(new BMenuItem(B_TRANSLATE("Find"),
448 		new BMessage(EDIT_SHOW_FIND_GROUP), 'F'));
449 	menu->AddItem(fFindPreviousMenuItem
450 		= new BMenuItem(B_TRANSLATE("Find previous"),
451 		new BMessage(EDIT_FIND_PREVIOUS), 'G', B_SHIFT_KEY));
452 	menu->AddItem(fFindNextMenuItem = new BMenuItem(B_TRANSLATE("Find next"),
453 		new BMessage(EDIT_FIND_NEXT), 'G'));
454 	mainMenu->AddItem(menu);
455 	fFindPreviousMenuItem->SetEnabled(false);
456 	fFindNextMenuItem->SetEnabled(false);
457 
458 	menu = new BMenu(B_TRANSLATE("View"));
459 	menu->AddItem(new BMenuItem(B_TRANSLATE("Reload"), new BMessage(RELOAD),
460 		'R'));
461 	// the label will be replaced with the appropriate text later on
462 	fBookmarkBarMenuItem = new BMenuItem(B_TRANSLATE("Show bookmark bar"),
463 		new BMessage(SHOW_HIDE_BOOKMARK_BAR));
464 	menu->AddItem(fBookmarkBarMenuItem);
465 	menu->AddSeparatorItem();
466 	menu->AddItem(new BMenuItem(B_TRANSLATE("Increase size"),
467 		new BMessage(ZOOM_FACTOR_INCREASE), '+'));
468 	menu->AddItem(new BMenuItem(B_TRANSLATE("Decrease size"),
469 		new BMessage(ZOOM_FACTOR_DECREASE), '-'));
470 	menu->AddItem(new BMenuItem(B_TRANSLATE("Reset size"),
471 		new BMessage(ZOOM_FACTOR_RESET), '0'));
472 	fZoomTextOnlyMenuItem = new BMenuItem(B_TRANSLATE("Zoom text only"),
473 		new BMessage(ZOOM_TEXT_ONLY));
474 	fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly);
475 	menu->AddItem(fZoomTextOnlyMenuItem);
476 
477 	menu->AddSeparatorItem();
478 	fFullscreenItem = new BMenuItem(B_TRANSLATE("Full screen"),
479 		new BMessage(TOGGLE_FULLSCREEN), B_RETURN);
480 	menu->AddItem(fFullscreenItem);
481 	menu->AddItem(new BMenuItem(B_TRANSLATE("Page source"),
482 		new BMessage(SHOW_PAGE_SOURCE), 'U'));
483 	mainMenu->AddItem(menu);
484 
485 	fHistoryMenu = new BMenu(B_TRANSLATE("History"));
486 	fHistoryMenu->AddItem(fBackMenuItem = new BMenuItem(B_TRANSLATE("Back"),
487 		new BMessage(GO_BACK), B_LEFT_ARROW));
488 	fHistoryMenu->AddItem(fForwardMenuItem
489 		= new BMenuItem(B_TRANSLATE("Forward"), new BMessage(GO_FORWARD),
490 		B_RIGHT_ARROW));
491 	fHistoryMenu->AddSeparatorItem();
492 	fHistoryMenuFixedItemCount = fHistoryMenu->CountItems();
493 	mainMenu->AddItem(fHistoryMenu);
494 
495 	BPath bookmarkPath;
496 	entry_ref bookmarkRef;
497 	if (_BookmarkPath(bookmarkPath) == B_OK
498 		&& get_ref_for_path(bookmarkPath.Path(), &bookmarkRef) == B_OK) {
499 		BMenu* bookmarkMenu
500 			= new BookmarkMenu(B_TRANSLATE("Bookmarks"), this, &bookmarkRef);
501 		mainMenu->AddItem(bookmarkMenu);
502 
503 		BDirectory barDir(&bookmarkRef);
504 		BEntry bookmarkBar(&barDir, kBookmarkBarSubdir);
505 		entry_ref bookmarkBarRef;
506 		// TODO we could also check if the folder is empty here.
507 		if (bookmarkBar.Exists() && bookmarkBar.GetRef(&bookmarkBarRef)
508 				== B_OK) {
509 			fBookmarkBar = new BookmarkBar("Bookmarks", this, &bookmarkBarRef);
510 			fBookmarkBarMenuItem->SetEnabled(true);
511 		} else
512 			fBookmarkBarMenuItem->SetEnabled(false);
513 	} else
514 		fBookmarkBarMenuItem->SetEnabled(false);
515 
516 	// Back, Forward, Stop & Home buttons
517 	fBackButton = new BIconButton("Back", NULL, new BMessage(GO_BACK));
518 	fBackButton->SetIcon(201);
519 	fBackButton->TrimIcon();
520 
521 	fForwardButton = new BIconButton("Forward", NULL, new BMessage(GO_FORWARD));
522 	fForwardButton->SetIcon(202);
523 	fForwardButton->TrimIcon();
524 
525 	fStopButton = new BIconButton("Stop", NULL, new BMessage(STOP));
526 	fStopButton->SetIcon(204);
527 	fStopButton->TrimIcon();
528 
529 	fHomeButton = new BIconButton("Home", NULL, new BMessage(HOME));
530 	fHomeButton->SetIcon(206);
531 	fHomeButton->TrimIcon();
532 	if (!fAppSettings->GetValue(kSettingsKeyShowHomeButton, true))
533 		fHomeButton->Hide();
534 
535 	// URL input group
536 	fURLInputGroup = new URLInputGroup(new BMessage(GOTO_URL));
537 
538 	// Status Bar
539 	fStatusText = new BStringView("status", "");
540 	fStatusText->SetAlignment(B_ALIGN_LEFT);
541 	fStatusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
542 	fStatusText->SetExplicitMinSize(BSize(150, 12));
543 		// Prevent the window from growing to fit a long status message...
544 	BFont font(be_plain_font);
545 	font.SetSize(ceilf(font.Size() * 0.8));
546 	fStatusText->SetFont(&font, B_FONT_SIZE);
547 
548 	// Loading progress bar
549 	fLoadingProgressBar = new BStatusBar("progress");
550 	fLoadingProgressBar->SetMaxValue(100);
551 	fLoadingProgressBar->Hide();
552 	font_height height;
553 	font.GetHeight(&height);
554 	fLoadingProgressBar->SetBarHeight(height.ascent + height.descent);
555 
556 	const float kInsetSpacing = 3;
557 	const float kElementSpacing = 5;
558 
559 	// Find group
560 	fFindCloseButton = new CloseButton(new BMessage(EDIT_HIDE_FIND_GROUP));
561 	fFindTextControl = new BTextControl("find", B_TRANSLATE("Find:"), "", NULL);
562 	fFindTextControl->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED));
563 	fFindPreviousButton = new BButton(B_TRANSLATE("Previous"),
564 		new BMessage(EDIT_FIND_PREVIOUS));
565 	fFindPreviousButton->SetToolTip(
566 		B_TRANSLATE_COMMENT("Find previous occurrence of search terms",
567 			"find bar previous button tooltip"));
568 	fFindNextButton = new BButton(B_TRANSLATE("Next"),
569 		new BMessage(EDIT_FIND_NEXT));
570 	fFindNextButton->SetToolTip(
571 		B_TRANSLATE_COMMENT("Find next occurrence of search terms",
572 			"find bar next button tooltip"));
573 	fFindCaseSensitiveCheckBox = new BCheckBox(B_TRANSLATE("Match case"));
574 	BGroupLayout* findGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
575 		.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
576 		.Add(BGroupLayoutBuilder(B_HORIZONTAL, B_USE_SMALL_SPACING)
577 			.Add(fFindCloseButton)
578 			.Add(fFindTextControl)
579 			.Add(fFindPreviousButton)
580 			.Add(fFindNextButton)
581 			.Add(fFindCaseSensitiveCheckBox)
582 			.SetInsets(kInsetSpacing, kInsetSpacing,
583 				kInsetSpacing, kInsetSpacing)
584 		)
585 	;
586 
587 	// Navigation group
588 	BGroupLayout* navigationGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
589 		.Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing)
590 			.Add(fBackButton)
591 			.Add(fForwardButton)
592 			.Add(fStopButton)
593 			.Add(fHomeButton)
594 			.Add(fURLInputGroup)
595 			.SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing,
596 				kInsetSpacing)
597 		)
598 		.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
599 	;
600 
601 	// Status bar group
602 	BGroupLayout* statusGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0)
603 		.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
604 		.Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing)
605 			.Add(fStatusText)
606 			.Add(fLoadingProgressBar, 0.2)
607 			.AddStrut(12 - kElementSpacing)
608 			.SetInsets(kInsetSpacing, 0, kInsetSpacing, 0)
609 		)
610 	;
611 
612 	BBitmapButton* toggleFullscreenButton = new BBitmapButton(kWindowIconBits,
613 		kWindowIconWidth, kWindowIconHeight, kWindowIconFormat,
614 		new BMessage(TOGGLE_FULLSCREEN));
615 	toggleFullscreenButton->SetBackgroundMode(BBitmapButton::MENUBAR_BACKGROUND);
616 
617 #if !INTEGRATE_MENU_INTO_TAB_BAR
618 	BMenu* mainMenuItem = mainMenu;
619 	fMenuGroup = (new BGroupView(B_HORIZONTAL, 0))->GroupLayout();
620 #else
621 	BMenu* mainMenuItem = new BMenuBar("Main menu");
622 	mainMenuItem->AddItem(mainMenu);
623 	fMenuGroup = fTabManager->MenuContainerLayout();
624 #endif
625 	BLayoutBuilder::Group<>(fMenuGroup)
626 		.Add(mainMenuItem)
627 		.Add(toggleFullscreenButton, 0.0f)
628 	;
629 
630 	if (fAppSettings->GetValue(kSettingsShowBookmarkBar, true))
631 		_ShowBookmarkBar(true);
632 	else
633 		_ShowBookmarkBar(false);
634 
635 	fSavePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this), NULL, 0,
636 		false);
637 
638 	// Layout
639 	BGroupView* topView = new BGroupView(B_VERTICAL, 0.0);
640 
641 #if !INTEGRATE_MENU_INTO_TAB_BAR
642 	topView->AddChild(fMenuGroup);
643 #endif
644 	topView->AddChild(fTabManager->TabGroup());
645 	topView->AddChild(navigationGroup);
646 	if (fBookmarkBar != NULL)
647 		topView->AddChild(fBookmarkBar);
648 	topView->AddChild(fTabManager->ContainerView());
649 	topView->AddChild(findGroup);
650 	topView->AddChild(statusGroup);
651 
652 	AddChild(topView);
653 
654 	fURLInputGroup->MakeFocus(true);
655 
656 	fTabGroup = fTabManager->TabGroup()->GetLayout();
657 	fNavigationGroup = navigationGroup;
658 	fFindGroup = findGroup;
659 	fStatusGroup = statusGroup;
660 	fToggleFullscreenButton = layoutItemFor(toggleFullscreenButton);
661 
662 	fFindGroup->SetVisible(false);
663 	fToggleFullscreenButton->SetVisible(false);
664 
665 	CreateNewTab(url, true, webView);
666 	_ShowInterface(true);
667 	_SetAutoHideInterfaceInFullscreen(fAppSettings->GetValue(
668 		kSettingsKeyAutoHideInterfaceInFullscreenMode,
669 		fAutoHideInterfaceInFullscreenMode));
670 
671 	AddShortcut('F', B_COMMAND_KEY | B_SHIFT_KEY,
672 		new BMessage(EDIT_HIDE_FIND_GROUP));
673 	// TODO: Should be a different shortcut, H is usually for Find selection.
674 	AddShortcut('H', B_COMMAND_KEY,	new BMessage(HOME));
675 
676 	// Add shortcuts to select a particular tab
677 	for (int32 i = 1; i <= 9; i++) {
678 		BMessage* selectTab = new BMessage(SELECT_TAB);
679 		selectTab->AddInt32("tab index", i - 1);
680 		char numStr[2];
681 		snprintf(numStr, sizeof(numStr), "%d", (int) i);
682 		AddShortcut(numStr[0], B_COMMAND_KEY, selectTab);
683 	}
684 
685 	// Add shortcut to cycle through tabs like in every other web browser
686 	AddShortcut(B_TAB, B_COMMAND_KEY, new BMessage(CYCLE_TABS));
687 
688 	BKeymap keymap;
689 	keymap.SetToCurrent();
690 	BObjectList<const char> unmodified(3, true);
691 	if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified)
692 			== B_OK) {
693 		int32 count = unmodified.CountItems();
694 		for (int32 i = 0; i < count; i++) {
695 			uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i));
696 			if (!HasShortcut(key, 0)) {
697 				// Add semantic zoom in shortcut, bug #7428
698 				AddShortcut(key, B_COMMAND_KEY,
699 					new BMessage(ZOOM_FACTOR_INCREASE));
700 			}
701 		}
702 	}
703 	unmodified.MakeEmpty();
704 
705 	be_app->PostMessage(WINDOW_OPENED);
706 }
707 
708 
~BrowserWindow()709 BrowserWindow::~BrowserWindow()
710 {
711 	fAppSettings->RemoveListener(BMessenger(this));
712 	delete fTabManager;
713 	delete fPulseRunner;
714 	delete fSavePanel;
715 }
716 
717 
718 void
DispatchMessage(BMessage * message,BHandler * target)719 BrowserWindow::DispatchMessage(BMessage* message, BHandler* target)
720 {
721 	const char* bytes;
722 	int32 modifierKeys;
723 	if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)
724 		&& message->FindString("bytes", &bytes) == B_OK
725 		&& message->FindInt32("modifiers", &modifierKeys) == B_OK) {
726 		if (bytes[0] == B_FUNCTION_KEY) {
727 			// Some function key Firefox compatibility
728 			int32 key;
729 			if (message->FindInt32("key", &key) == B_OK) {
730 				switch (key) {
731 					case B_F5_KEY:
732 						PostMessage(RELOAD);
733 						break;
734 
735 					case B_F11_KEY:
736 						PostMessage(TOGGLE_FULLSCREEN);
737 						break;
738 
739 					default:
740 						break;
741 				}
742 			}
743 		} else if (target == fURLInputGroup->TextView()) {
744 			// Handle B_RETURN in the URL text control. This is the easiest
745 			// way to react *only* when the user presses the return key in the
746 			// address bar, as opposed to trying to load whatever is in there
747 			// when the text control just goes out of focus.
748 			if (bytes[0] == B_RETURN) {
749 				// Do it in such a way that the user sees the Go-button go down.
750 				_InvokeButtonVisibly(fURLInputGroup->GoButton());
751 				return;
752 			} else if (bytes[0] == B_ESCAPE) {
753 				// Replace edited text with the current URL.
754 				fURLInputGroup->LockURLInput(false);
755 				fURLInputGroup->SetText(CurrentWebView()->MainFrameURL());
756 			}
757 		} else if (target == fFindTextControl->TextView()) {
758 			// Handle B_RETURN when the find text control has focus.
759 			if (bytes[0] == B_RETURN) {
760 				if ((modifierKeys & B_SHIFT_KEY) != 0)
761 					_InvokeButtonVisibly(fFindPreviousButton);
762 				else
763 					_InvokeButtonVisibly(fFindNextButton);
764 				return;
765 			} else if (bytes[0] == B_ESCAPE) {
766 				_InvokeButtonVisibly(fFindCloseButton);
767 				return;
768 			}
769 		} else if (bytes[0] == B_ESCAPE && !fMenusRunning) {
770 			if (modifierKeys == B_COMMAND_KEY)
771 				_ShowInterface(true);
772 			else {
773 				// Default escape key behavior:
774 				PostMessage(STOP);
775 				return;
776 			}
777 		}
778 	}
779 
780 	if (message->what == B_MOUSE_MOVED || message->what == B_MOUSE_DOWN
781 		|| message->what == B_MOUSE_UP) {
782 		message->FindPoint("where", &fLastMousePos);
783 		if (message->FindInt64("when", &fLastMouseMovedTime) != B_OK)
784 			fLastMouseMovedTime = system_time();
785 		_CheckAutoHideInterface();
786 	}
787 
788 	if (message->what == B_MOUSE_WHEEL_CHANGED) {
789 		BPoint where;
790 		uint32 buttons;
791 		CurrentWebView()->GetMouse(&where, &buttons, false);
792 		// Only do this when the mouse is over the web view
793 		if (CurrentWebView()->Bounds().Contains(where)) {
794 			// Zoom and unzoom text on Command + mouse wheel.
795 			// This could of course (and maybe should be) implemented in the
796 			// WebView, but there would need to be a way for the WebView to
797 			// know the setting of the fZoomTextOnly member here. Plus other
798 			// clients of the API may not want this feature.
799 			if ((modifiers() & B_COMMAND_KEY) != 0) {
800 				float deltaY;
801 				if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK) {
802 					if (deltaY < 0)
803 						CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly);
804 					else
805 						CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly);
806 
807 					return;
808 				}
809 			}
810 		} else {
811 			// Also don't scroll up and down if the mouse is not over the
812 			// web view
813 			return;
814 		}
815 	}
816 
817 	BWebWindow::DispatchMessage(message, target);
818 }
819 
820 
821 void
MessageReceived(BMessage * message)822 BrowserWindow::MessageReceived(BMessage* message)
823 {
824 	switch (message->what) {
825 		case OPEN_LOCATION:
826 			_ShowInterface(true);
827 			if (fURLInputGroup->TextView()->IsFocus())
828 				fURLInputGroup->TextView()->SelectAll();
829 			else
830 				fURLInputGroup->MakeFocus(true);
831 			break;
832 
833 		case RELOAD:
834 			CurrentWebView()->Reload();
835 			break;
836 
837 		case SHOW_HIDE_BOOKMARK_BAR:
838 			_ShowBookmarkBar(fBookmarkBar->IsHidden());
839 			break;
840 
841 		case GOTO_URL:
842 		{
843 			BString url;
844 			if (message->FindString("url", &url) != B_OK)
845 				url = fURLInputGroup->Text();
846 
847 			_SetPageIcon(CurrentWebView(), NULL);
848 			_SmartURLHandler(url);
849 
850 			break;
851 		}
852 
853 		case SAVE_PAGE:
854 		{
855 			fSavePanel->SetSaveText(CurrentWebView()->MainFrameTitle());
856 			fSavePanel->Show();
857 			break;
858 		}
859 
860 		case B_SAVE_REQUESTED:
861 		{
862 			entry_ref ref;
863 			BString name;
864 
865 			if (message->FindRef("directory", &ref) == B_OK
866 				&& message->FindString("name", &name) == B_OK) {
867 				BDirectory dir(&ref);
868 				BFile output(&dir, name,
869 					B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
870 				CurrentWebView()->WebPage()->GetContentsAsMHTML(output);
871 			}
872 
873 			break;
874 		}
875 
876 		case GO_BACK:
877 			CurrentWebView()->GoBack();
878 			break;
879 
880 		case GO_FORWARD:
881 			CurrentWebView()->GoForward();
882 			break;
883 
884 		case STOP:
885 			CurrentWebView()->StopLoading();
886 			break;
887 
888 		case HOME:
889 			CurrentWebView()->LoadURL(fStartPageURL);
890 			break;
891 
892 		case CLEAR_HISTORY: {
893 			BrowsingHistory* history = BrowsingHistory::DefaultInstance();
894 			if (history->CountItems() == 0)
895 				break;
896 			BAlert* alert = new BAlert(B_TRANSLATE("Confirmation"),
897 				B_TRANSLATE("Do you really want to "
898 				"clear the browsing history?"), B_TRANSLATE("Clear"),
899 				B_TRANSLATE("Cancel"));
900 			alert->SetShortcut(1, B_ESCAPE);
901 
902 			if (alert->Go() == 0)
903 				history->Clear();
904 			break;
905 		}
906 
907 		case CREATE_BOOKMARK:
908 			_CreateBookmark();
909 			break;
910 
911 		case SHOW_BOOKMARKS:
912 			_ShowBookmarks();
913 			break;
914 
915 		case B_REFS_RECEIVED:
916 		{
917 			// Currently the only source of these messages is the bookmarks
918 			// menu.
919 			// Filter refs into URLs, this also gets rid of refs for folders.
920 			// For clicks on sub-folders in the bookmarks menu, we have Tracker
921 			// open the corresponding folder.
922 			entry_ref ref;
923 			uint32 addedCount = 0;
924 			for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
925 				BEntry entry(&ref);
926 				uint32 addedSubCount = 0;
927 				if (entry.IsDirectory()) {
928 					BDirectory directory(&entry);
929 					_AddBookmarkURLsRecursively(directory, message,
930 						addedSubCount);
931 				} else {
932 					BFile file(&ref, B_READ_ONLY);
933 					BString url;
934 					if (_ReadURLAttr(file, url)) {
935 						message->AddString("url", url.String());
936 						addedSubCount++;
937 					}
938 				}
939 				if (addedSubCount == 0) {
940 					// Don't know what to do with this entry, just pass it
941 					// on to the system to handle. Note that this may result
942 					// in us opening other supported files via the application
943 					// mechanism.
944 					be_roster->Launch(&ref);
945 				}
946 				addedCount += addedSubCount;
947 			}
948 			message->RemoveName("refs");
949 			if (addedCount > 10) {
950 				BString string(B_TRANSLATE_COMMENT("Do you want to open "
951 					"%addedCount bookmarks all at once?", "Don't translate "
952 					"variable %addedCount."));
953 				string.ReplaceFirst("%addedCount", BString() << addedCount);
954 
955 				BAlert* alert = new BAlert(
956 					B_TRANSLATE("Open bookmarks confirmation"),
957 					string.String(), B_TRANSLATE("Cancel"),
958 					B_TRANSLATE("Open all"));
959 				alert->SetShortcut(0, B_ESCAPE);
960 				if (alert->Go() == 0)
961 					break;
962 			}
963 			message->AddPointer("window", this);
964 			be_app->PostMessage(message);
965 			break;
966 		}
967 
968 		case B_SIMPLE_DATA:
969 		{
970 			const char* filetype = message->GetString("be:filetypes");
971 			if (filetype != NULL
972 				&& strcmp(filetype, "application/x-vnd.Be-bookmark") == 0
973 				&& LastMouseMovedView() == fBookmarkBar) {
974 				// Something that can be made into a bookmark (e.g. the page icon)
975 				// was dragged and dropped on the bookmark bar.
976 				BPath path;
977 				if (_BookmarkPath(path) == B_OK && path.Append(kBookmarkBarSubdir) == B_OK) {
978 					entry_ref ref;
979 					if (BEntry(path.Path()).GetRef(&ref) == B_OK) {
980 						message->AddRef("directory", &ref);
981 							// Add under the same name that Tracker would use, if
982 							// the ref had been added by dragging and dropping to Tracker.
983 						_CreateBookmark(message);
984 					}
985 				}
986 				break;
987 			}
988 
989 			// User possibly dropped files on this window.
990 			// If there is more than one entry_ref, let the app handle it
991 			// (open one new page per ref). If there is one ref, open it in
992 			// this window.
993 			type_code type;
994 			int32 countFound;
995 			if (message->GetInfo("refs", &type, &countFound) != B_OK
996 				|| type != B_REF_TYPE) {
997 				break;
998 			}
999 			if (countFound > 1) {
1000 				message->what = B_REFS_RECEIVED;
1001 				be_app->PostMessage(message);
1002 				break;
1003 			}
1004 			entry_ref ref;
1005 			if (message->FindRef("refs", &ref) != B_OK)
1006 				break;
1007 			BEntry entry(&ref, true);
1008 			BPath path;
1009 			if (!entry.Exists() || entry.GetPath(&path) != B_OK)
1010 				break;
1011 
1012 			BUrl url(path);
1013 			CurrentWebView()->LoadURL(url);
1014 			break;
1015 		}
1016 
1017 		case ZOOM_FACTOR_INCREASE:
1018 			CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly);
1019 			break;
1020 		case ZOOM_FACTOR_DECREASE:
1021 			CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly);
1022 			break;
1023 		case ZOOM_FACTOR_RESET:
1024 			CurrentWebView()->ResetZoomFactor();
1025 			break;
1026 		case ZOOM_TEXT_ONLY:
1027 			fZoomTextOnly = !fZoomTextOnly;
1028 			fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly);
1029 			// TODO: Would be nice to have an instant update if the page is
1030 			// already zoomed.
1031 			break;
1032 
1033 		case TOGGLE_FULLSCREEN:
1034 			ToggleFullscreen();
1035 			break;
1036 
1037 		case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN:
1038 			_SetAutoHideInterfaceInFullscreen(
1039 				!fAutoHideInterfaceInFullscreenMode);
1040 			break;
1041 
1042 		case CHECK_AUTO_HIDE_INTERFACE:
1043 			_CheckAutoHideInterface();
1044 			break;
1045 
1046 		case SHOW_PAGE_SOURCE:
1047 			CurrentWebView()->WebPage()->SendPageSource();
1048 			break;
1049 		case B_PAGE_SOURCE_RESULT:
1050 			_HandlePageSourceResult(message);
1051 			break;
1052 
1053 		case EDIT_FIND_NEXT:
1054 			CurrentWebView()->FindString(fFindTextControl->Text(), true,
1055 				fFindCaseSensitiveCheckBox->Value());
1056 			break;
1057 		case FIND_TEXT_CHANGED:
1058 		{
1059 			bool findTextAvailable = strlen(fFindTextControl->Text()) > 0;
1060 			fFindPreviousMenuItem->SetEnabled(findTextAvailable);
1061 			fFindNextMenuItem->SetEnabled(findTextAvailable);
1062 			break;
1063 		}
1064 		case EDIT_FIND_PREVIOUS:
1065 			CurrentWebView()->FindString(fFindTextControl->Text(), false,
1066 				fFindCaseSensitiveCheckBox->Value());
1067 			break;
1068 		case EDIT_SHOW_FIND_GROUP:
1069 			if (!fFindGroup->IsVisible())
1070 				fFindGroup->SetVisible(true);
1071 			fFindTextControl->MakeFocus(true);
1072 			break;
1073 		case EDIT_HIDE_FIND_GROUP:
1074 			if (fFindGroup->IsVisible()) {
1075 				fFindGroup->SetVisible(false);
1076 				if (CurrentWebView() != NULL)
1077 					CurrentWebView()->MakeFocus(true);
1078 			}
1079 			break;
1080 
1081 		case B_CUT:
1082 		case B_COPY:
1083 		case B_PASTE:
1084 		{
1085 			BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
1086 			if (textView != NULL)
1087 				textView->MessageReceived(message);
1088 			else if (CurrentWebView() != NULL)
1089 				CurrentWebView()->MessageReceived(message);
1090 			break;
1091 		}
1092 
1093 		case B_EDITING_CAPABILITIES_RESULT:
1094 		{
1095 			BWebView* webView;
1096 			if (message->FindPointer("view",
1097 					reinterpret_cast<void**>(&webView)) != B_OK
1098 				|| webView != CurrentWebView()) {
1099 				break;
1100 			}
1101 			bool canCut;
1102 			bool canCopy;
1103 			bool canPaste;
1104 			if (message->FindBool("can cut", &canCut) != B_OK)
1105 				canCut = false;
1106 			if (message->FindBool("can copy", &canCopy) != B_OK)
1107 				canCopy = false;
1108 			if (message->FindBool("can paste", &canPaste) != B_OK)
1109 				canPaste = false;
1110 			fCutMenuItem->SetEnabled(canCut);
1111 			fCopyMenuItem->SetEnabled(canCopy);
1112 			fPasteMenuItem->SetEnabled(canPaste);
1113 			break;
1114 		}
1115 
1116 		case SHOW_DOWNLOAD_WINDOW:
1117 		case SHOW_SETTINGS_WINDOW:
1118 		case SHOW_CONSOLE_WINDOW:
1119 		case SHOW_COOKIE_WINDOW:
1120 			message->AddUInt32("workspaces", Workspaces());
1121 			be_app->PostMessage(message);
1122 			break;
1123 
1124 		case CLOSE_TAB:
1125 			if (fTabManager->CountTabs() > 1) {
1126 				int32 index;
1127 				if (message->FindInt32("tab index", &index) != B_OK)
1128 					index = fTabManager->SelectedTabIndex();
1129 				_ShutdownTab(index);
1130 				_UpdateTabGroupVisibility();
1131 			} else
1132 				PostMessage(B_QUIT_REQUESTED);
1133 			break;
1134 
1135 		case SELECT_TAB:
1136 		{
1137 			int32 index;
1138 			if (message->FindInt32("tab index", &index) == B_OK
1139 				&& fTabManager->SelectedTabIndex() != index
1140 				&& fTabManager->CountTabs() > index) {
1141 				fTabManager->SelectTab(index);
1142 			}
1143 
1144 			break;
1145 		}
1146 
1147 		case CYCLE_TABS:
1148 		{
1149 			int32 index = fTabManager->SelectedTabIndex() + 1;
1150 			if (index >= fTabManager->CountTabs())
1151 				index = 0;
1152 			fTabManager->SelectTab(index);
1153 			break;
1154 		}
1155 
1156 		case TAB_CHANGED:
1157 		{
1158 			// This message may be received also when the last tab closed,
1159 			// i.e. with index == -1.
1160 			int32 index;
1161 			if (message->FindInt32("tab index", &index) != B_OK)
1162 				index = -1;
1163 			_TabChanged(index);
1164 			break;
1165 		}
1166 
1167 		case SETTINGS_VALUE_CHANGED:
1168 		{
1169 			BString name;
1170 			if (message->FindString("name", &name) != B_OK)
1171 				break;
1172 			bool flag;
1173 			BString string;
1174 			uint32 value;
1175 			if (name == kSettingsKeyShowTabsIfSinglePageOpen
1176 				&& message->FindBool("value", &flag) == B_OK) {
1177 				if (fShowTabsIfSinglePageOpen != flag) {
1178 					fShowTabsIfSinglePageOpen = flag;
1179 					_UpdateTabGroupVisibility();
1180 				}
1181 			} else if (name == kSettingsKeyAutoHidePointer
1182 				&& message->FindBool("value", &flag) == B_OK) {
1183 				fAutoHidePointer = flag;
1184 				if (CurrentWebView())
1185 					CurrentWebView()->SetAutoHidePointer(fAutoHidePointer);
1186 			} else if (name == kSettingsKeyStartPageURL
1187 				&& message->FindString("value", &string) == B_OK) {
1188 				fStartPageURL = string;
1189 			} else if (name == kSettingsKeySearchPageURL
1190 				&& message->FindString("value", &string) == B_OK) {
1191 				fSearchPageURL = string;
1192 			} else if (name == kSettingsKeyNewWindowPolicy
1193 				&& message->FindUInt32("value", &value) == B_OK) {
1194 				fNewWindowPolicy = value;
1195 			} else if (name == kSettingsKeyNewTabPolicy
1196 				&& message->FindUInt32("value", &value) == B_OK) {
1197 				fNewTabPolicy = value;
1198 			} else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode
1199 				&& message->FindBool("value", &flag) == B_OK) {
1200 				_SetAutoHideInterfaceInFullscreen(flag);
1201 			} else if (name == kSettingsKeyShowHomeButton
1202 				&& message->FindBool("value", &flag) == B_OK) {
1203 				if (flag)
1204 					fHomeButton->Show();
1205 				else
1206 					fHomeButton->Hide();
1207 			} else if (name == kSettingsShowBookmarkBar
1208 				&& message->FindBool("value", &flag) == B_OK) {
1209 				_ShowBookmarkBar(flag);
1210 			}
1211 			break;
1212 		}
1213 		case ADD_CONSOLE_MESSAGE:
1214 			be_app->PostMessage(message);
1215 			BWebWindow::MessageReceived(message);
1216 			break;
1217 
1218 		case B_COPY_TARGET:
1219 		{
1220 			const char* filetype = message->GetString("be:filetypes");
1221 			if (filetype != NULL && strcmp(filetype, "application/x-vnd.Be-bookmark") == 0) {
1222 				// Tracker replied after the user dragged and dropped something
1223 				// that can be bookmarked (e.g. the page icon) to a Tracker window.
1224 				_CreateBookmark(message);
1225 				break;
1226 			} else {
1227 				BWebWindow::MessageReceived(message);
1228 				break;
1229 			}
1230 		}
1231 
1232 		default:
1233 			BWebWindow::MessageReceived(message);
1234 			break;
1235 	}
1236 }
1237 
1238 
1239 status_t
Archive(BMessage * archive,bool deep) const1240 BrowserWindow::Archive(BMessage* archive, bool deep) const
1241 {
1242 	status_t ret = archive->AddRect("window frame", Frame());
1243 
1244 	for (int i = 0; i < fTabManager->CountTabs(); i++) {
1245 		BWebView* view = dynamic_cast<BWebView*>(fTabManager->ViewForTab(i));
1246 		if (view == NULL) {
1247 			continue;
1248 		}
1249 
1250 		if (ret == B_OK)
1251 			ret = archive->AddString("tab", view->MainFrameURL());
1252 	}
1253 
1254 	return ret;
1255 }
1256 
1257 
1258 bool
QuitRequested()1259 BrowserWindow::QuitRequested()
1260 {
1261 	// TODO: Check for modified form data and ask user for confirmation, etc.
1262 
1263 	BMessage message(WINDOW_CLOSED);
1264 	Archive(&message);
1265 
1266 	// Iterate over all tabs to delete all BWebViews.
1267 	// Do this here, so WebKit tear down happens earlier.
1268 	SetCurrentWebView(NULL);
1269 	while (fTabManager->CountTabs() > 0)
1270 		_ShutdownTab(0);
1271 
1272 	message.AddRect("window frame", WindowFrame());
1273 	be_app->PostMessage(&message);
1274 	return true;
1275 }
1276 
1277 
1278 void
MenusBeginning()1279 BrowserWindow::MenusBeginning()
1280 {
1281 	_UpdateHistoryMenu();
1282 	_UpdateClipboardItems();
1283 	fMenusRunning = true;
1284 }
1285 
1286 
1287 void
MenusEnded()1288 BrowserWindow::MenusEnded()
1289 {
1290 	fMenusRunning = false;
1291 }
1292 
1293 
1294 void
ScreenChanged(BRect screenSize,color_space format)1295 BrowserWindow::ScreenChanged(BRect screenSize, color_space format)
1296 {
1297 	if (fIsFullscreen)
1298 		_ResizeToScreen();
1299 }
1300 
1301 
1302 void
WorkspacesChanged(uint32 oldWorkspaces,uint32 newWorkspaces)1303 BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1304 {
1305 	if (fIsFullscreen)
1306 		_ResizeToScreen();
1307 }
1308 
1309 
1310 static bool
viewIsChild(const BView * parent,const BView * view)1311 viewIsChild(const BView* parent, const BView* view)
1312 {
1313 	if (parent == view)
1314 		return true;
1315 
1316 	int32 count = parent->CountChildren();
1317 	for (int32 i = 0; i < count; i++) {
1318 		BView* child = parent->ChildAt(i);
1319 		if (viewIsChild(child, view))
1320 			return true;
1321 	}
1322 	return false;
1323 }
1324 
1325 
1326 void
SetCurrentWebView(BWebView * webView)1327 BrowserWindow::SetCurrentWebView(BWebView* webView)
1328 {
1329 	if (webView == CurrentWebView())
1330 		return;
1331 
1332 	if (CurrentWebView() != NULL) {
1333 		// Remember the currently focused view before switching tabs,
1334 		// so that we can revert the focus when switching back to this tab
1335 		// later.
1336 		PageUserData* userData = static_cast<PageUserData*>(
1337 			CurrentWebView()->GetUserData());
1338 		if (userData == NULL) {
1339 			userData = new PageUserData(CurrentFocus());
1340 			CurrentWebView()->SetUserData(userData);
1341 		}
1342 		userData->SetFocusedView(CurrentFocus());
1343 		userData->SetURLInputContents(fURLInputGroup->Text());
1344 		int32 selectionStart;
1345 		int32 selectionEnd;
1346 		fURLInputGroup->TextView()->GetSelection(&selectionStart,
1347 			&selectionEnd);
1348 		userData->SetURLInputSelection(selectionStart, selectionEnd);
1349 	}
1350 
1351 	BWebWindow::SetCurrentWebView(webView);
1352 
1353 	if (webView != NULL) {
1354 		webView->SetAutoHidePointer(fAutoHidePointer);
1355 
1356 		_UpdateTitle(webView->MainFrameTitle());
1357 
1358 		// Restore the previous focus or focus the web view.
1359 		PageUserData* userData = static_cast<PageUserData*>(
1360 			webView->GetUserData());
1361 		BView* focusedView = NULL;
1362 		if (userData != NULL)
1363 			focusedView = userData->FocusedView();
1364 
1365 		if (focusedView != NULL
1366 			&& viewIsChild(GetLayout()->View(), focusedView)) {
1367 			focusedView->MakeFocus(true);
1368 		} else
1369 			webView->MakeFocus(true);
1370 
1371 		bool state = fURLInputGroup->IsURLInputLocked();
1372 		fURLInputGroup->LockURLInput(false);
1373 			// Unlock it so the following code can update the URL
1374 
1375 		if (userData != NULL) {
1376 			fURLInputGroup->SetPageIcon(userData->PageIcon());
1377 			if (userData->URLInputContents().Length())
1378 				fURLInputGroup->SetText(userData->URLInputContents());
1379 			else
1380 				fURLInputGroup->SetText(webView->MainFrameURL());
1381 			if (userData->URLInputSelectionStart() >= 0) {
1382 				fURLInputGroup->TextView()->Select(
1383 					userData->URLInputSelectionStart(),
1384 					userData->URLInputSelectionEnd());
1385 			}
1386 		} else {
1387 			fURLInputGroup->SetPageIcon(NULL);
1388 			fURLInputGroup->SetText(webView->MainFrameURL());
1389 		}
1390 
1391 		fURLInputGroup->LockURLInput(state);
1392 			// Restore the state
1393 
1394 		// Trigger update of the interface to the new page, by requesting
1395 		// to resend all notifications.
1396 		webView->WebPage()->ResendNotifications();
1397 	} else
1398 		_UpdateTitle("");
1399 }
1400 
1401 
1402 bool
IsBlankTab() const1403 BrowserWindow::IsBlankTab() const
1404 {
1405 	if (CurrentWebView() == NULL)
1406 		return false;
1407 	BString requestedURL = CurrentWebView()->MainFrameRequestedURL();
1408 	return requestedURL.Length() == 0
1409 		|| requestedURL == _NewTabURL(fTabManager->CountTabs() == 1);
1410 }
1411 
1412 
1413 void
CreateNewTab(const BString & _url,bool select,BWebView * webView)1414 BrowserWindow::CreateNewTab(const BString& _url, bool select,
1415 	BWebView* webView)
1416 {
1417 	bool applyNewPagePolicy = webView == NULL;
1418 	// Executed in app thread (new BWebPage needs to be created in app thread).
1419 	if (webView == NULL)
1420 		webView = new BWebView("web view", fContext);
1421 
1422 	bool isNewWindow = fTabManager->CountTabs() == 0;
1423 
1424 	fTabManager->AddTab(webView, B_TRANSLATE("New tab"));
1425 
1426 	BString url(_url);
1427 	if (applyNewPagePolicy && url.Length() == 0)
1428 		url = _NewTabURL(isNewWindow);
1429 
1430 	if (url.Length() > 0)
1431 		webView->LoadURL(url.String());
1432 
1433 	if (select) {
1434 		fTabManager->SelectTab(fTabManager->CountTabs() - 1);
1435 		SetCurrentWebView(webView);
1436 		webView->WebPage()->ResendNotifications();
1437 		fURLInputGroup->SetPageIcon(NULL);
1438 		fURLInputGroup->SetText(url.String());
1439 		fURLInputGroup->MakeFocus(true);
1440 	}
1441 
1442 	_ShowInterface(true);
1443 	_UpdateTabGroupVisibility();
1444 }
1445 
1446 
1447 BRect
WindowFrame() const1448 BrowserWindow::WindowFrame() const
1449 {
1450 	if (fIsFullscreen)
1451 		return fNonFullscreenWindowFrame;
1452 	else
1453 		return Frame();
1454 }
1455 
1456 
1457 void
ToggleFullscreen()1458 BrowserWindow::ToggleFullscreen()
1459 {
1460 	if (fIsFullscreen) {
1461 		MoveTo(fNonFullscreenWindowFrame.LeftTop());
1462 		ResizeTo(fNonFullscreenWindowFrame.Width(),
1463 			fNonFullscreenWindowFrame.Height());
1464 
1465 		SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
1466 		SetLook(B_DOCUMENT_WINDOW_LOOK);
1467 
1468 		_ShowInterface(true);
1469 	} else {
1470 		fNonFullscreenWindowFrame = Frame();
1471 		_ResizeToScreen();
1472 
1473 		SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE));
1474 		SetLook(B_TITLED_WINDOW_LOOK);
1475 	}
1476 	fIsFullscreen = !fIsFullscreen;
1477 	fFullscreenItem->SetMarked(fIsFullscreen);
1478 	fToggleFullscreenButton->SetVisible(fIsFullscreen);
1479 }
1480 
1481 
1482 // #pragma mark - Notification API
1483 
1484 
1485 void
NavigationRequested(const BString & url,BWebView * view)1486 BrowserWindow::NavigationRequested(const BString& url, BWebView* view)
1487 {
1488 }
1489 
1490 
1491 void
NewWindowRequested(const BString & url,bool primaryAction)1492 BrowserWindow::NewWindowRequested(const BString& url, bool primaryAction)
1493 {
1494 	// Always open new windows in the application thread, since
1495 	// creating a BWebView will try to grab the application lock.
1496 	// But our own WebPage may already try to lock us from within
1497 	// the application thread -> dead-lock. Thus we can't wait for
1498 	// a reply here.
1499 	BMessage message(NEW_TAB);
1500 	message.AddPointer("window", this);
1501 	message.AddString("url", url);
1502 	message.AddBool("select", primaryAction);
1503 	be_app->PostMessage(&message);
1504 }
1505 
1506 
1507 void
NewPageCreated(BWebView * view,BRect windowFrame,bool modalDialog,bool resizable,bool activate)1508 BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame,
1509 	bool modalDialog, bool resizable, bool activate)
1510 {
1511 	if (windowFrame.IsValid()) {
1512 		BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings,
1513 			BString(), fContext, INTERFACE_ELEMENT_STATUS,
1514 			view);
1515 		window->Show();
1516 	} else
1517 		CreateNewTab(BString(), activate, view);
1518 }
1519 
1520 
1521 void
CloseWindowRequested(BWebView * view)1522 BrowserWindow::CloseWindowRequested(BWebView* view)
1523 {
1524 	int32 index = fTabManager->TabForView(view);
1525 	if (index < 0) {
1526 		// Tab is already gone.
1527 		return;
1528 	}
1529 	BMessage message(CLOSE_TAB);
1530 	message.AddInt32("tab index", index);
1531 	PostMessage(&message, this);
1532 }
1533 
1534 
1535 void
LoadNegotiating(const BString & url,BWebView * view)1536 BrowserWindow::LoadNegotiating(const BString& url, BWebView* view)
1537 {
1538 	if (view != CurrentWebView()) {
1539 		// Update the userData contents instead so the user sees
1540 		// the correct URL when they switch back to that tab.
1541 		PageUserData* userData = static_cast<PageUserData*>(
1542 			view->GetUserData());
1543 		if (userData != NULL && userData->URLInputContents().Length() == 0) {
1544 			userData->SetURLInputContents(url);
1545 		}
1546 	}
1547 
1548 	fURLInputGroup->SetText(url.String());
1549 
1550 	BString status(B_TRANSLATE("Requesting %url"));
1551 	status.ReplaceFirst("%url", url);
1552 	view->WebPage()->SetStatusMessage(status);
1553 }
1554 
1555 
1556 void
LoadCommitted(const BString & url,BWebView * view)1557 BrowserWindow::LoadCommitted(const BString& url, BWebView* view)
1558 {
1559 	if (view != CurrentWebView())
1560 		return;
1561 
1562 	// This hook is invoked when the load is committed.
1563 	fURLInputGroup->SetText(url.String());
1564 
1565 	BString status(B_TRANSLATE("Loading %url"));
1566 	status.ReplaceFirst("%url", url);
1567 	view->WebPage()->SetStatusMessage(status);
1568 }
1569 
1570 
1571 void
LoadProgress(float progress,BWebView * view)1572 BrowserWindow::LoadProgress(float progress, BWebView* view)
1573 {
1574 	if (view != CurrentWebView())
1575 		return;
1576 
1577 	if (progress < 100 && fLoadingProgressBar->IsHidden())
1578 		_ShowProgressBar(true);
1579 	else if (progress == 100 && !fLoadingProgressBar->IsHidden())
1580 		_ShowProgressBar(false);
1581 	fLoadingProgressBar->SetTo(progress);
1582 }
1583 
1584 
1585 void
LoadFailed(const BString & url,BWebView * view)1586 BrowserWindow::LoadFailed(const BString& url, BWebView* view)
1587 {
1588 	if (view != CurrentWebView())
1589 		return;
1590 
1591 	BString status(B_TRANSLATE_COMMENT("%url failed", "Loading URL failed. "
1592 		"Don't translate variable %url."));
1593 	status.ReplaceFirst("%url", url);
1594 	view->WebPage()->SetStatusMessage(status);
1595 	if (!fLoadingProgressBar->IsHidden())
1596 		fLoadingProgressBar->Hide();
1597 }
1598 
1599 
1600 void
LoadFinished(const BString & url,BWebView * view)1601 BrowserWindow::LoadFinished(const BString& url, BWebView* view)
1602 {
1603 	if (view != CurrentWebView())
1604 		return;
1605 
1606 	fURLInputGroup->SetText(url.String());
1607 
1608 	BString status(B_TRANSLATE_COMMENT("%url finished", "Loading URL "
1609 		"finished. Don't translate variable %url."));
1610 	status.ReplaceFirst("%url", url);
1611 	view->WebPage()->SetStatusMessage(status);
1612 	if (!fLoadingProgressBar->IsHidden())
1613 		fLoadingProgressBar->Hide();
1614 
1615 	NavigationCapabilitiesChanged(fBackButton->IsEnabled(),
1616 		fForwardButton->IsEnabled(), false, view);
1617 
1618 	int32 tabIndex = fTabManager->TabForView(view);
1619 	if (tabIndex > 0 && strcmp(B_TRANSLATE("New tab"),
1620 		fTabManager->TabLabel(tabIndex)) == 0)
1621 			fTabManager->SetTabLabel(tabIndex, url);
1622 }
1623 
1624 
1625 void
MainDocumentError(const BString & failingURL,const BString & localizedDescription,BWebView * view)1626 BrowserWindow::MainDocumentError(const BString& failingURL,
1627 	const BString& localizedDescription, BWebView* view)
1628 {
1629 	// Make sure we show the page that contains the view.
1630 	if (!_ShowPage(view))
1631 		return;
1632 
1633 	// Try delegating the URL to an external app instead.
1634 	int32 at = failingURL.FindFirst(":");
1635 	if (at > 0) {
1636 		BString proto;
1637 		failingURL.CopyInto(proto, 0, at);
1638 
1639 		bool handled = false;
1640 
1641 		for (unsigned int i = 0; i < sizeof(kHandledProtocols) / sizeof(char*);
1642 				i++) {
1643 			handled = (proto == kHandledProtocols[i]);
1644 			if (handled)
1645 				break;
1646 		}
1647 
1648 		if (!handled) {
1649 			_SmartURLHandler(failingURL);
1650 			return;
1651 		}
1652 	}
1653 
1654 	BWebWindow::MainDocumentError(failingURL, localizedDescription, view);
1655 
1656 	// TODO: Remove the failing URL from the BrowsingHistory!
1657 }
1658 
1659 
1660 void
TitleChanged(const BString & title,BWebView * view)1661 BrowserWindow::TitleChanged(const BString& title, BWebView* view)
1662 {
1663 	int32 tabIndex = fTabManager->TabForView(view);
1664 	if (tabIndex < 0)
1665 		return;
1666 
1667 	fTabManager->SetTabLabel(tabIndex, title);
1668 
1669 	if (view != CurrentWebView())
1670 		return;
1671 
1672 	_UpdateTitle(title);
1673 }
1674 
1675 
1676 void
IconReceived(const BBitmap * icon,BWebView * view)1677 BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view)
1678 {
1679 	// The view may already be gone, since this notification arrives
1680 	// asynchronously.
1681 	if (!fTabManager->HasView(view))
1682 		return;
1683 
1684 	_SetPageIcon(view, icon);
1685 }
1686 
1687 
1688 void
ResizeRequested(float width,float height,BWebView * view)1689 BrowserWindow::ResizeRequested(float width, float height, BWebView* view)
1690 {
1691 	if (view != CurrentWebView())
1692 		return;
1693 
1694 	// Ignore request when there is more than one BWebView embedded.
1695 	if (fTabManager->CountTabs() > 1)
1696 		return;
1697 
1698 	// Make sure the new frame is not larger than the screen frame minus
1699 	// window decorator border.
1700 	BScreen screen(this);
1701 	BRect screenFrame = screen.Frame();
1702 	BRect decoratorFrame = DecoratorFrame();
1703 	BRect frame = Frame();
1704 
1705 	screenFrame.left += decoratorFrame.left - frame.left;
1706 	screenFrame.right += decoratorFrame.right - frame.right;
1707 	screenFrame.top += decoratorFrame.top - frame.top;
1708 	screenFrame.bottom += decoratorFrame.bottom - frame.bottom;
1709 
1710 	width = min_c(width, screen.Frame().Width());
1711 	height = min_c(height, screen.Frame().Height());
1712 
1713 	frame.right = frame.left + width;
1714 	frame.bottom = frame.top + height;
1715 
1716 	// frame is now not larger than screenFrame, but may still be partly outside
1717 	if (!screenFrame.Contains(frame)) {
1718 		if (frame.left < screenFrame.left)
1719 			frame.OffsetBy(screenFrame.left - frame.left, 0);
1720 		else if (frame.right > screenFrame.right)
1721 			frame.OffsetBy(screenFrame.right - frame.right, 0);
1722 		if (frame.top < screenFrame.top)
1723 			frame.OffsetBy(screenFrame.top - frame.top, 0);
1724 		else if (frame.bottom > screenFrame.bottom)
1725 			frame.OffsetBy(screenFrame.bottom - frame.bottom, 0);
1726 	}
1727 
1728 	MoveTo(frame.left, frame.top);
1729 	ResizeTo(width, height);
1730 }
1731 
1732 
1733 void
SetToolBarsVisible(bool flag,BWebView * view)1734 BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view)
1735 {
1736 	// TODO
1737 	// TODO: Ignore request when there is more than one BWebView embedded!
1738 }
1739 
1740 
1741 void
SetStatusBarVisible(bool flag,BWebView * view)1742 BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view)
1743 {
1744 	// TODO
1745 	// TODO: Ignore request when there is more than one BWebView embedded!
1746 }
1747 
1748 
1749 void
SetMenuBarVisible(bool flag,BWebView * view)1750 BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view)
1751 {
1752 	// TODO
1753 	// TODO: Ignore request when there is more than one BWebView embedded!
1754 }
1755 
1756 
1757 void
SetResizable(bool flag,BWebView * view)1758 BrowserWindow::SetResizable(bool flag, BWebView* view)
1759 {
1760 	// TODO: Ignore request when there is more than one BWebView embedded!
1761 
1762 	if (flag)
1763 		SetFlags(Flags() & ~B_NOT_RESIZABLE);
1764 	else
1765 		SetFlags(Flags() | B_NOT_RESIZABLE);
1766 }
1767 
1768 
1769 void
StatusChanged(const BString & statusText,BWebView * view)1770 BrowserWindow::StatusChanged(const BString& statusText, BWebView* view)
1771 {
1772 	if (view != CurrentWebView())
1773 		return;
1774 
1775 	if (fStatusText)
1776 		fStatusText->SetText(statusText.String());
1777 }
1778 
1779 
1780 void
NavigationCapabilitiesChanged(bool canGoBackward,bool canGoForward,bool canStop,BWebView * view)1781 BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward,
1782 	bool canGoForward, bool canStop, BWebView* view)
1783 {
1784 	if (view != CurrentWebView())
1785 		return;
1786 
1787 	fBackButton->SetEnabled(canGoBackward);
1788 	fForwardButton->SetEnabled(canGoForward);
1789 	fStopButton->SetEnabled(canStop);
1790 
1791 	fBackMenuItem->SetEnabled(canGoBackward);
1792 	fForwardMenuItem->SetEnabled(canGoForward);
1793 }
1794 
1795 
1796 void
UpdateGlobalHistory(const BString & url)1797 BrowserWindow::UpdateGlobalHistory(const BString& url)
1798 {
1799 	BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url));
1800 
1801 	fURLInputGroup->SetText(CurrentWebView()->MainFrameURL());
1802 }
1803 
1804 
1805 bool
AuthenticationChallenge(BString message,BString & inOutUser,BString & inOutPassword,bool & inOutRememberCredentials,uint32 failureCount,BWebView * view)1806 BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser,
1807 	BString& inOutPassword, bool& inOutRememberCredentials,
1808 	uint32 failureCount, BWebView* view)
1809 {
1810 	CredentialsStorage* persistentStorage
1811 		= CredentialsStorage::PersistentInstance();
1812 	CredentialsStorage* sessionStorage
1813 		= CredentialsStorage::SessionInstance();
1814 
1815 	// TODO: Using the message as key here is not so smart.
1816 	HashString key(message);
1817 
1818 	if (failureCount == 0) {
1819 		if (persistentStorage->Contains(key)) {
1820 			Credentials credentials = persistentStorage->GetCredentials(key);
1821 			inOutUser = credentials.Username();
1822 			inOutPassword = credentials.Password();
1823 			return true;
1824 		} else if (sessionStorage->Contains(key)) {
1825 			Credentials credentials = sessionStorage->GetCredentials(key);
1826 			inOutUser = credentials.Username();
1827 			inOutPassword = credentials.Password();
1828 			return true;
1829 		}
1830 	}
1831 	// Switch to the page for which this authentication is required.
1832 	if (!_ShowPage(view))
1833 		return false;
1834 
1835 	AuthenticationPanel* panel = new AuthenticationPanel(Frame());
1836 		// Panel auto-destructs.
1837 	bool success = panel->getAuthentication(message, inOutUser, inOutPassword,
1838 		inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword,
1839 		&inOutRememberCredentials);
1840 	if (success) {
1841 		Credentials credentials(inOutUser, inOutPassword);
1842 		if (inOutRememberCredentials)
1843 			persistentStorage->PutCredentials(key, credentials);
1844 		else
1845 			sessionStorage->PutCredentials(key, credentials);
1846 	}
1847 	return success;
1848 }
1849 
1850 
1851 // #pragma mark - private
1852 
1853 
1854 void
_UpdateTitle(const BString & title)1855 BrowserWindow::_UpdateTitle(const BString& title)
1856 {
1857 	BString windowTitle;
1858 
1859 	if (title.Length() > 0)
1860 		windowTitle = title;
1861 	else {
1862 		BWebView* webView = CurrentWebView();
1863 		if (webView != NULL) {
1864 			BString url = webView->MainFrameURL();
1865 			int32 leafPos = url.FindLast('/');
1866 			url.Remove(0, leafPos + 1);
1867 			windowTitle = url;
1868 		}
1869 	}
1870 
1871 	if (windowTitle.Length() > 0)
1872 		windowTitle << " - ";
1873 	windowTitle << kApplicationName;
1874 	SetTitle(windowTitle.String());
1875 }
1876 
1877 
1878 void
_UpdateTabGroupVisibility()1879 BrowserWindow::_UpdateTabGroupVisibility()
1880 {
1881 	if (Lock()) {
1882 		if (fInterfaceVisible)
1883 			fTabGroup->SetVisible(_TabGroupShouldBeVisible());
1884 		fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1);
1885 		Unlock();
1886 	}
1887 }
1888 
1889 
1890 bool
_TabGroupShouldBeVisible() const1891 BrowserWindow::_TabGroupShouldBeVisible() const
1892 {
1893 	return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1)
1894 		&& (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0;
1895 }
1896 
1897 
1898 void
_ShutdownTab(int32 index)1899 BrowserWindow::_ShutdownTab(int32 index)
1900 {
1901 	BView* view = fTabManager->RemoveTab(index);
1902 	BWebView* webView = dynamic_cast<BWebView*>(view);
1903 	if (webView == CurrentWebView())
1904 		SetCurrentWebView(NULL);
1905 	if (webView != NULL)
1906 		webView->Shutdown();
1907 	else
1908 		delete view;
1909 }
1910 
1911 
1912 void
_TabChanged(int32 index)1913 BrowserWindow::_TabChanged(int32 index)
1914 {
1915 	SetCurrentWebView(dynamic_cast<BWebView*>(fTabManager->ViewForTab(index)));
1916 }
1917 
1918 
1919 status_t
_BookmarkPath(BPath & path) const1920 BrowserWindow::_BookmarkPath(BPath& path) const
1921 {
1922 	status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
1923 	if (ret != B_OK)
1924 		return ret;
1925 
1926 	ret = path.Append(kApplicationName);
1927 	if (ret != B_OK)
1928 		return ret;
1929 
1930 	ret = path.Append("Bookmarks");
1931 	if (ret != B_OK)
1932 		return ret;
1933 
1934 	return create_directory(path.Path(), 0777);
1935 }
1936 
1937 /*! If fileName is an empty BString, a valid file name will be derived from title.
1938 	miniIcon and largeIcon may be NULL.
1939 */
1940 void
_CreateBookmark(const BPath & path,BString fileName,const BString & title,const BString & url,const BBitmap * miniIcon,const BBitmap * largeIcon)1941 BrowserWindow::_CreateBookmark(const BPath& path, BString fileName, const BString& title,
1942 	const BString& url, const BBitmap* miniIcon, const BBitmap* largeIcon)
1943 {
1944 	// Determine the file name if one was not provided
1945 	bool presetFileName = true;
1946 	if (fileName.IsEmpty() == true) {
1947 		presetFileName = false;
1948 		fileName = title;
1949 		if (fileName.Length() == 0) {
1950 			fileName = url;
1951 			int32 leafPos = fileName.FindLast('/');
1952 			if (leafPos >= 0)
1953 				fileName.Remove(0, leafPos + 1);
1954 		}
1955 		fileName.ReplaceAll('/', '-');
1956 		fileName.Truncate(B_FILE_NAME_LENGTH - 1);
1957 	}
1958 
1959 	BPath entryPath(path);
1960 	status_t status = entryPath.Append(fileName);
1961 	BEntry entry;
1962 	if (status == B_OK)
1963 		status = entry.SetTo(entryPath.Path(), true);
1964 
1965 	// There are several reasons why an entry matching the path argument could already exist.
1966 	if (status == B_OK && entry.Exists() == true) {
1967 		off_t size;
1968 		entry.GetSize(&size);
1969 		char attrName[B_ATTR_NAME_LENGTH];
1970 		BNode node(&entry);
1971 		status_t attrStatus = node.GetNextAttrName(attrName);
1972 		if (strcmp(attrName, "_trk/pinfo_le") == 0)
1973 			attrStatus = node.GetNextAttrName(attrName);
1974 
1975 		if (presetFileName == true && size == 0 && attrStatus == B_ENTRY_NOT_FOUND) {
1976 			// Tracker's drag-and-drop routine created an empty entry for us to fill in.
1977 			// Go ahead and write to the existing entry.
1978 		} else {
1979 			BDirectory directory(path.Path());
1980 			if (_CheckBookmarkExists(directory, fileName, url) == true) {
1981 				// The existing entry is a bookmark with the same URL.  No further action needed.
1982 				return;
1983 			} else {
1984 				// Find a unique name for the bookmark.
1985 				int32 tries = 1;
1986 				while (entry.Exists()) {
1987 					fileName << " " << tries++;
1988 					entryPath = path;
1989 					status = entryPath.Append(fileName);
1990 					if (status == B_OK)
1991 						status = entry.SetTo(entryPath.Path(), true);
1992 					if (status != B_OK)
1993 						break;
1994 				}
1995 			}
1996 		}
1997 	}
1998 
1999 	BFile bookmarkFile;
2000 	if (status == B_OK) {
2001 		status = bookmarkFile.SetTo(&entry,
2002 			B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
2003 	}
2004 
2005 	// Write bookmark meta data
2006 	if (status == B_OK)
2007 		status = bookmarkFile.WriteAttrString("META:url", &url);
2008 	if (status == B_OK) {
2009 		bookmarkFile.WriteAttrString("META:title", &title);
2010 	}
2011 
2012 	BNodeInfo nodeInfo(&bookmarkFile);
2013 	if (status == B_OK) {
2014 		status = nodeInfo.SetType("application/x-vnd.Be-bookmark");
2015 		// Replace the standard Be-bookmark file icons with the argument icons,
2016 		// if any were provided.
2017 		if (status == B_OK) {
2018 			status_t ret = B_OK;
2019 			if (miniIcon != NULL) {
2020 				ret = nodeInfo.SetIcon(miniIcon, B_MINI_ICON);
2021 				if (ret != B_OK) {
2022 					fprintf(stderr, "Failed to store mini icon for bookmark: "
2023 						"%s\n", strerror(ret));
2024 				}
2025 			}
2026 			if (largeIcon != NULL && ret == B_OK)
2027 				ret = nodeInfo.SetIcon(largeIcon, B_LARGE_ICON);
2028 			else if (largeIcon == NULL && miniIcon != NULL && ret == B_OK) {
2029 				// If largeIcon is not available but miniIcon is, use a magnified miniIcon instead.
2030 				BBitmap substituteLargeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK,
2031 					B_CMAP8);
2032 				const uint8* src = (const uint8*)miniIcon->Bits();
2033 				uint32 srcBPR = miniIcon->BytesPerRow();
2034 				uint8* dst = (uint8*)substituteLargeIcon.Bits();
2035 				uint32 dstBPR = substituteLargeIcon.BytesPerRow();
2036 				for (uint32 y = 0; y < 16; y++) {
2037 					const uint8* s = src;
2038 					uint8* d = dst;
2039 					for (uint32 x = 0; x < 16; x++) {
2040 						*d++ = *s;
2041 						*d++ = *s++;
2042 					}
2043 					dst += dstBPR;
2044 					s = src;
2045 					for (uint32 x = 0; x < 16; x++) {
2046 						*d++ = *s;
2047 						*d++ = *s++;
2048 					}
2049 					dst += dstBPR;
2050 					src += srcBPR;
2051 				}
2052 				ret = nodeInfo.SetIcon(&substituteLargeIcon, B_LARGE_ICON);
2053 			} else
2054 				ret = B_OK;
2055 			if (ret != B_OK) {
2056 				fprintf(stderr, "Failed to store large icon for bookmark: "
2057 					"%s\n", strerror(ret));
2058 			}
2059 		}
2060 	}
2061 
2062 	if (status != B_OK) {
2063 		BString message(B_TRANSLATE_COMMENT("There was an error creating the "
2064 			"bookmark file.\n\nError: %error", "Don't translate variable "
2065 			"%error"));
2066 		message.ReplaceFirst("%error", strerror(status));
2067 		BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"),
2068 			message.String(), B_TRANSLATE("OK"), NULL, NULL,
2069 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
2070 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2071 		alert->Go();
2072 		return;
2073 	}
2074 }
2075 
2076 
2077 void
_CreateBookmark(BMessage * message)2078 BrowserWindow::_CreateBookmark(BMessage* message)
2079 {
2080 		entry_ref ref;
2081 		BMessage originatorData;
2082 		const char* url;
2083 		const char* title;
2084 		bool validData = (message->FindRef("directory", &ref) == B_OK
2085 			&& message->FindMessage("be:originator-data", &originatorData) == B_OK
2086 			&& originatorData.FindString("url", &url) == B_OK
2087 			&& originatorData.FindString("title", &title) == B_OK);
2088 
2089 		// Optional data
2090 		const char* fileName;
2091 		if (message->FindString("name", &fileName) != B_OK) {
2092 			// This string is only present if the message originated from Tracker (drag and drop).
2093 			fileName = "";
2094 		}
2095 		const BBitmap* miniIcon = NULL;
2096 		const BBitmap* largeIcon = NULL;
2097 		originatorData.FindData("miniIcon", B_COLOR_8_BIT_TYPE,
2098 			reinterpret_cast<const void**>(&miniIcon), NULL);
2099 		originatorData.FindData("largeIcon", B_COLOR_8_BIT_TYPE,
2100 			reinterpret_cast<const void**>(&miniIcon), NULL);
2101 
2102 		if (validData == true) {
2103 			_CreateBookmark(BPath(&ref), BString(fileName), BString(title), BString(url),
2104 				miniIcon, largeIcon);
2105 		} else {
2106 			BString message(B_TRANSLATE("There was an error setting up "
2107 				"the bookmark."));
2108 			BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"),
2109 				message.String(), B_TRANSLATE("OK"), NULL, NULL,
2110 				B_WIDTH_AS_USUAL, B_STOP_ALERT);
2111 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2112 			alert->Go();
2113 		}
2114 		return;
2115 }
2116 
2117 
2118 void
_CreateBookmark()2119 BrowserWindow::_CreateBookmark()
2120 {
2121 	BString fileName;
2122 		// A name will be derived from the title later.
2123 	BString title(CurrentWebView()->MainFrameTitle());
2124 	BString url(CurrentWebView()->MainFrameURL());
2125 	BPath path;
2126 	status_t status = _BookmarkPath(path);
2127 
2128 	BBitmap* miniIcon = NULL;
2129 	BBitmap* largeIcon = NULL;
2130 	PageUserData* userData = static_cast<PageUserData*>(CurrentWebView()->GetUserData());
2131 	if (userData != NULL && userData->PageIcon() != NULL) {
2132 		miniIcon = new BBitmap(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK, B_CMAP8);
2133 		miniIcon->ImportBits(userData->PageIcon());
2134 		// TODO:  retrieve the large icon too, once PageUserData can provide it.
2135 	}
2136 
2137 	if (status == B_OK)
2138 		_CreateBookmark(path, fileName, title, url, miniIcon, largeIcon);
2139 	else {
2140 		BString message(B_TRANSLATE_COMMENT("There was an error retrieving "
2141 			"the bookmark folder.\n\nError: %error", "Don't translate the "
2142 			"variable %error"));
2143 		message.ReplaceFirst("%error", strerror(status));
2144 		BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"),
2145 			message.String(), B_TRANSLATE("OK"), NULL, NULL,
2146 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
2147 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2148 		alert->Go();
2149 	}
2150 	return;
2151 }
2152 
2153 
2154 void
_ShowBookmarks()2155 BrowserWindow::_ShowBookmarks()
2156 {
2157 	BPath path;
2158 	entry_ref ref;
2159 	status_t status = _BookmarkPath(path);
2160 	if (status == B_OK)
2161 		status = get_ref_for_path(path.Path(), &ref);
2162 	if (status == B_OK)
2163 		status = be_roster->Launch(&ref);
2164 
2165 	if (status != B_OK && status != B_ALREADY_RUNNING) {
2166 		BString message(B_TRANSLATE_COMMENT("There was an error trying to "
2167 			"show the Bookmarks folder.\n\nError: %error",
2168 			"Don't translate variable %error"));
2169 		message.ReplaceFirst("%error", strerror(status));
2170 		BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"),
2171 			message.String(), B_TRANSLATE("OK"), NULL, NULL,
2172 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
2173 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2174 		alert->Go();
2175 		return;
2176 	}
2177 }
2178 
2179 
_CheckBookmarkExists(BDirectory & directory,const BString & bookmarkName,const BString & url) const2180 bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory,
2181 	const BString& bookmarkName, const BString& url) const
2182 {
2183 	BEntry entry;
2184 	while (directory.GetNextEntry(&entry) == B_OK) {
2185 		char entryName[B_FILE_NAME_LENGTH];
2186 		if (entry.GetName(entryName) != B_OK || bookmarkName != entryName)
2187 			continue;
2188 		BString storedURL;
2189 		BFile file(&entry, B_READ_ONLY);
2190 		if (_ReadURLAttr(file, storedURL)) {
2191 			// Just bail if the bookmark already exists
2192 			if (storedURL == url)
2193 				return true;
2194 		}
2195 	}
2196 	return false;
2197 }
2198 
2199 
2200 bool
_ReadURLAttr(BFile & bookmarkFile,BString & url) const2201 BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const
2202 {
2203 	return bookmarkFile.InitCheck() == B_OK
2204 		&& bookmarkFile.ReadAttrString("META:url", &url) == B_OK;
2205 }
2206 
2207 
2208 void
_AddBookmarkURLsRecursively(BDirectory & directory,BMessage * message,uint32 & addedCount) const2209 BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory,
2210 	BMessage* message, uint32& addedCount) const
2211 {
2212 	BEntry entry;
2213 	while (directory.GetNextEntry(&entry) == B_OK) {
2214 		if (entry.IsDirectory()) {
2215 			BDirectory subBirectory(&entry);
2216 			// At least preserve the entry file handle when recursing into
2217 			// sub-folders... eventually we will run out, though, with very
2218 			// deep hierarchy.
2219 			entry.Unset();
2220 			_AddBookmarkURLsRecursively(subBirectory, message, addedCount);
2221 		} else {
2222 			BString storedURL;
2223 			BFile file(&entry, B_READ_ONLY);
2224 			if (_ReadURLAttr(file, storedURL)) {
2225 				message->AddString("url", storedURL.String());
2226 				addedCount++;
2227 			}
2228 		}
2229 	}
2230 }
2231 
2232 
2233 void
_SetPageIcon(BWebView * view,const BBitmap * icon)2234 BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon)
2235 {
2236 	PageUserData* userData = static_cast<PageUserData*>(view->GetUserData());
2237 	if (userData == NULL) {
2238 		userData = new(std::nothrow) PageUserData(NULL);
2239 		if (userData == NULL)
2240 			return;
2241 		view->SetUserData(userData);
2242 	}
2243 	// The PageUserData makes a copy of the icon, which we pass on to
2244 	// the TabManager for display in the respective tab.
2245 	userData->SetPageIcon(icon);
2246 	fTabManager->SetTabIcon(view, userData->PageIcon());
2247 	if (view == CurrentWebView())
2248 		fURLInputGroup->SetPageIcon(icon);
2249 }
2250 
2251 
2252 static void
addItemToMenuOrSubmenu(BMenu * menu,BMenuItem * newItem)2253 addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem)
2254 {
2255 	BString baseURLLabel = baseURL(BString(newItem->Label()));
2256 	for (int32 i = menu->CountItems() - 1; i >= 0; i--) {
2257 		BMenuItem* item = menu->ItemAt(i);
2258 		BString label = item->Label();
2259 		if (label.FindFirst(baseURLLabel) >= 0) {
2260 			if (item->Submenu()) {
2261 				// Submenu was already added in previous iteration.
2262 				item->Submenu()->AddItem(newItem);
2263 				return;
2264 			} else {
2265 				menu->RemoveItem(item);
2266 				BMenu* subMenu = new BMenu(baseURLLabel.String());
2267 				subMenu->AddItem(item);
2268 				subMenu->AddItem(newItem);
2269 				// Add common submenu for this base URL, clickable.
2270 				BMessage* message = new BMessage(GOTO_URL);
2271 				message->AddString("url", baseURLLabel.String());
2272 				menu->AddItem(new BMenuItem(subMenu, message), i);
2273 				return;
2274 			}
2275 		}
2276 	}
2277 	menu->AddItem(newItem);
2278 }
2279 
2280 
2281 static void
addOrDeleteMenu(BMenu * menu,BMenu * toMenu)2282 addOrDeleteMenu(BMenu* menu, BMenu* toMenu)
2283 {
2284 	if (menu->CountItems() > 0)
2285 		toMenu->AddItem(menu);
2286 	else
2287 		delete menu;
2288 }
2289 
2290 
2291 void
_UpdateHistoryMenu()2292 BrowserWindow::_UpdateHistoryMenu()
2293 {
2294 	BMenuItem* menuItem;
2295 	while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount)))
2296 		delete menuItem;
2297 
2298 	BrowsingHistory* history = BrowsingHistory::DefaultInstance();
2299 	if (!history->Lock())
2300 		return;
2301 
2302 	int32 count = history->CountItems();
2303 	BMenuItem* clearHistoryItem = new BMenuItem(B_TRANSLATE("Clear history"),
2304 		new BMessage(CLEAR_HISTORY));
2305 	clearHistoryItem->SetEnabled(count > 0);
2306 	fHistoryMenu->AddItem(clearHistoryItem);
2307 	if (count == 0) {
2308 		history->Unlock();
2309 		return;
2310 	}
2311 	fHistoryMenu->AddSeparatorItem();
2312 
2313 	BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME);
2314 	todayStart.SetTime(BTime(0, 0, 0));
2315 
2316 	BDateTime oneDayAgoStart = todayStart;
2317 	oneDayAgoStart.Date().AddDays(-1);
2318 
2319 	BDateTime twoDaysAgoStart = oneDayAgoStart;
2320 	twoDaysAgoStart.Date().AddDays(-1);
2321 
2322 	BDateTime threeDaysAgoStart = twoDaysAgoStart;
2323 	threeDaysAgoStart.Date().AddDays(-1);
2324 
2325 	BDateTime fourDaysAgoStart = threeDaysAgoStart;
2326 	fourDaysAgoStart.Date().AddDays(-1);
2327 
2328 	BDateTime fiveDaysAgoStart = fourDaysAgoStart;
2329 	fiveDaysAgoStart.Date().AddDays(-1);
2330 
2331 	BMenu* todayMenu = new BMenu(B_TRANSLATE("Today"));
2332 	BMenu* yesterdayMenu = new BMenu(B_TRANSLATE("Yesterday"));
2333 	BMenu* twoDaysAgoMenu = new BMenu(
2334 		twoDaysAgoStart.Date().LongDayName().String());
2335 	BMenu* threeDaysAgoMenu = new BMenu(
2336 		threeDaysAgoStart.Date().LongDayName().String());
2337 	BMenu* fourDaysAgoMenu = new BMenu(
2338 		fourDaysAgoStart.Date().LongDayName().String());
2339 	BMenu* fiveDaysAgoMenu = new BMenu(
2340 		fiveDaysAgoStart.Date().LongDayName().String());
2341 	BMenu* earlierMenu = new BMenu(B_TRANSLATE("Earlier"));
2342 
2343 	for (int32 i = 0; i < count; i++) {
2344 		BrowsingHistoryItem historyItem = history->HistoryItemAt(i);
2345 		BMessage* message = new BMessage(GOTO_URL);
2346 		message->AddString("url", historyItem.URL().String());
2347 
2348 		BString truncatedUrl(historyItem.URL());
2349 		be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480);
2350 		menuItem = new BMenuItem(truncatedUrl, message);
2351 
2352 		if (historyItem.DateTime() < fiveDaysAgoStart)
2353 			addItemToMenuOrSubmenu(earlierMenu, menuItem);
2354 		else if (historyItem.DateTime() < fourDaysAgoStart)
2355 			addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem);
2356 		else if (historyItem.DateTime() < threeDaysAgoStart)
2357 			addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem);
2358 		else if (historyItem.DateTime() < twoDaysAgoStart)
2359 			addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem);
2360 		else if (historyItem.DateTime() < oneDayAgoStart)
2361 			addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem);
2362 		else if (historyItem.DateTime() < todayStart)
2363 			addItemToMenuOrSubmenu(yesterdayMenu, menuItem);
2364 		else
2365 			addItemToMenuOrSubmenu(todayMenu, menuItem);
2366 	}
2367 	history->Unlock();
2368 
2369 	addOrDeleteMenu(todayMenu, fHistoryMenu);
2370 	addOrDeleteMenu(yesterdayMenu, fHistoryMenu);
2371 	addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu);
2372 	addOrDeleteMenu(threeDaysAgoMenu, fHistoryMenu);
2373 	addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu);
2374 	addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu);
2375 	addOrDeleteMenu(earlierMenu, fHistoryMenu);
2376 }
2377 
2378 
2379 void
_UpdateClipboardItems()2380 BrowserWindow::_UpdateClipboardItems()
2381 {
2382 	BTextView* focusTextView = dynamic_cast<BTextView*>(CurrentFocus());
2383 	if (focusTextView != NULL) {
2384 		int32 selectionStart;
2385 		int32 selectionEnd;
2386 		focusTextView->GetSelection(&selectionStart, &selectionEnd);
2387 		bool hasSelection = selectionStart < selectionEnd;
2388 		bool canPaste = false;
2389 		// A BTextView has the focus.
2390 		if (be_clipboard->Lock()) {
2391 			BMessage* data = be_clipboard->Data();
2392 			if (data != NULL)
2393 				canPaste = data->HasData("text/plain", B_MIME_TYPE);
2394 			be_clipboard->Unlock();
2395 		}
2396 		fCutMenuItem->SetEnabled(hasSelection);
2397 		fCopyMenuItem->SetEnabled(hasSelection);
2398 		fPasteMenuItem->SetEnabled(canPaste);
2399 	} else if (CurrentWebView() != NULL) {
2400 		// Trigger update of the clipboard items, even if the
2401 		// BWebView doesn't have focus, we'll dispatch these message
2402 		// there anyway. This works so fast that the user can never see
2403 		// the wrong enabled state when the menu opens until the result
2404 		// message arrives. The initial state needs to be enabled, since
2405 		// standard shortcut handling is always wrapped inside MenusBeginning()
2406 		// and MenusEnded(), and since we update items asynchronously, we need
2407 		// to have them enabled to begin with.
2408 		fCutMenuItem->SetEnabled(true);
2409 		fCopyMenuItem->SetEnabled(true);
2410 		fPasteMenuItem->SetEnabled(true);
2411 
2412 		CurrentWebView()->WebPage()->SendEditingCapabilities();
2413 	}
2414 }
2415 
2416 
2417 bool
_ShowPage(BWebView * view)2418 BrowserWindow::_ShowPage(BWebView* view)
2419 {
2420 	if (view != CurrentWebView()) {
2421 		int32 tabIndex = fTabManager->TabForView(view);
2422 		if (tabIndex < 0) {
2423 			// Page seems to be gone already?
2424 			return false;
2425 		}
2426 		fTabManager->SelectTab(tabIndex);
2427 		_TabChanged(tabIndex);
2428 		UpdateIfNeeded();
2429 	}
2430 	return true;
2431 }
2432 
2433 
2434 void
_ResizeToScreen()2435 BrowserWindow::_ResizeToScreen()
2436 {
2437 	BScreen screen(this);
2438 	MoveTo(0, 0);
2439 	ResizeTo(screen.Frame().Width(), screen.Frame().Height());
2440 }
2441 
2442 
2443 void
_SetAutoHideInterfaceInFullscreen(bool doIt)2444 BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt)
2445 {
2446 	if (fAutoHideInterfaceInFullscreenMode == doIt)
2447 		return;
2448 
2449 	fAutoHideInterfaceInFullscreenMode = doIt;
2450 	if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
2451 			doIt) != doIt) {
2452 		fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode,
2453 			doIt);
2454 	}
2455 
2456 	if (fAutoHideInterfaceInFullscreenMode) {
2457 		BMessage message(CHECK_AUTO_HIDE_INTERFACE);
2458 		fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000);
2459 	} else {
2460 		delete fPulseRunner;
2461 		fPulseRunner = NULL;
2462 		_ShowInterface(true);
2463 	}
2464 }
2465 
2466 
2467 void
_CheckAutoHideInterface()2468 BrowserWindow::_CheckAutoHideInterface()
2469 {
2470 	if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode
2471 		|| (CurrentWebView() != NULL && !CurrentWebView()->IsFocus())) {
2472 		return;
2473 	}
2474 
2475 	if (fLastMousePos.y == 0)
2476 		_ShowInterface(true);
2477 	else if (fNavigationGroup->IsVisible()
2478 		&& fLastMousePos.y > fNavigationGroup->Frame().bottom
2479 		&& system_time() - fLastMouseMovedTime > 1000000) {
2480 		// NOTE: Do not re-use navigationGroupBottom in the above
2481 		// check, since we only want to hide the interface when it is visible.
2482 		_ShowInterface(false);
2483 	}
2484 }
2485 
2486 
2487 void
_ShowInterface(bool show)2488 BrowserWindow::_ShowInterface(bool show)
2489 {
2490 	if (fInterfaceVisible == show)
2491 		return;
2492 
2493 	fInterfaceVisible = show;
2494 
2495 	if (show) {
2496 #if !INTEGRATE_MENU_INTO_TAB_BAR
2497 		fMenuGroup->SetVisible(
2498 			(fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0);
2499 #endif
2500 		fTabGroup->SetVisible(_TabGroupShouldBeVisible());
2501 		fNavigationGroup->SetVisible(
2502 			(fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0);
2503 		fStatusGroup->SetVisible(
2504 			(fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0);
2505 	} else {
2506 		fMenuGroup->SetVisible(false);
2507 		fTabGroup->SetVisible(false);
2508 		fNavigationGroup->SetVisible(false);
2509 		fStatusGroup->SetVisible(false);
2510 	}
2511 	// TODO: Setting the group visible seems to unhide the status bar.
2512 	// Fix in Haiku?
2513 	while (!fLoadingProgressBar->IsHidden())
2514 		fLoadingProgressBar->Hide();
2515 }
2516 
2517 
2518 void
_ShowProgressBar(bool show)2519 BrowserWindow::_ShowProgressBar(bool show)
2520 {
2521 	if (show) {
2522 		if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements
2523 			& INTERFACE_ELEMENT_STATUS) != 0)
2524 				fStatusGroup->SetVisible(true);
2525 		fLoadingProgressBar->Show();
2526 	} else {
2527 		if (!fInterfaceVisible)
2528 			fStatusGroup->SetVisible(false);
2529 		// TODO: This is also used in _ShowInterface. Without it the status bar
2530 		// doesn't always hide again. It may be an Interface Kit bug.
2531 		while (!fLoadingProgressBar->IsHidden())
2532 			fLoadingProgressBar->Hide();
2533 	}
2534 }
2535 
2536 
2537 void
_InvokeButtonVisibly(BButton * button)2538 BrowserWindow::_InvokeButtonVisibly(BButton* button)
2539 {
2540 	button->SetValue(B_CONTROL_ON);
2541 	UpdateIfNeeded();
2542 	button->Invoke();
2543 	snooze(1000);
2544 	button->SetValue(B_CONTROL_OFF);
2545 }
2546 
2547 
2548 BString
_NewTabURL(bool isNewWindow) const2549 BrowserWindow::_NewTabURL(bool isNewWindow) const
2550 {
2551 	BString url;
2552 	uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy;
2553 	// Implement new page policy
2554 	switch (policy) {
2555 		case OpenStartPage:
2556 			url = fStartPageURL;
2557 			break;
2558 		case OpenSearchPage:
2559 			url.SetTo(fSearchPageURL);
2560 			url.ReplaceAll("%s", "");
2561 			break;
2562 		case CloneCurrentPage:
2563 			if (CurrentWebView() != NULL)
2564 				url = CurrentWebView()->MainFrameURL();
2565 			break;
2566 		case OpenBlankPage:
2567 		default:
2568 			break;
2569 	}
2570 	return url;
2571 }
2572 
2573 
2574 BString
_EncodeURIComponent(const BString & search)2575 BrowserWindow::_EncodeURIComponent(const BString& search)
2576 {
2577 	// We have to take care of some of the escaping before we hand over the
2578 	// search string to WebKit, if we want queries like "4+3" to not be
2579 	// searched as "4 3".
2580 	const BString escCharList = " $&`:<>[]{}\"+#%@/;=?\\^|~\',";
2581 	BString result = search;
2582 	char hexcode[4];
2583 
2584 	for (int32 i = 0; i < result.Length(); i++) {
2585 		if (escCharList.FindFirst(result[i]) != B_ERROR) {
2586 			sprintf(hexcode, "%02X", (unsigned int)result[i]);
2587 			result.SetByteAt(i, '%');
2588 			result.Insert(hexcode, i + 1);
2589 			i += 2;
2590 		}
2591 	}
2592 
2593 	return result;
2594 }
2595 
2596 
2597 void
_VisitURL(const BString & url)2598 BrowserWindow::_VisitURL(const BString& url)
2599 {
2600 	// fURLInputGroup->TextView()->SetText(url);
2601 	CurrentWebView()->LoadURL(url.String());
2602 }
2603 
2604 
2605 void
_VisitSearchEngine(const BString & search)2606 BrowserWindow::_VisitSearchEngine(const BString& search)
2607 {
2608 	BString searchQuery = search;
2609 
2610 	BString searchPrefix;
2611 	search.CopyCharsInto(searchPrefix, 0, 2);
2612 
2613 	// Default search URL
2614 	BString engine(fSearchPageURL);
2615 
2616 	// Check if the string starts with one of the search engine shortcuts
2617 	for (int i = 0; kSearchEngines[i].url != NULL; i++) {
2618 		if (kSearchEngines[i].shortcut == searchPrefix) {
2619 			engine = kSearchEngines[i].url;
2620 			searchQuery.Remove(0, 2);
2621 			break;
2622 		}
2623 	}
2624 
2625 	engine.ReplaceAll("%s", _EncodeURIComponent(searchQuery));
2626 	_VisitURL(engine);
2627 }
2628 
2629 
2630 inline bool
_IsValidDomainChar(char ch)2631 BrowserWindow::_IsValidDomainChar(char ch)
2632 {
2633 	// TODO: Currenlty, only a whitespace character breaks a domain name. It
2634 	// might be a good idea (or a bad one) to make character filtering based on
2635 	// the IDNA 2008 standard.
2636 
2637 	return ch != ' ';
2638 }
2639 
2640 
2641 /*! \brief "smart" parser for user-entered URLs
2642 
2643 	We try to be flexible in what we accept as a valid URL. The protocol may
2644 	be missing, or something we can't handle (in that case we run the matching
2645 	app). If all attempts to make sense of the input fail, we make a search
2646 	engine query for it.
2647  */
2648 void
_SmartURLHandler(const BString & url)2649 BrowserWindow::_SmartURLHandler(const BString& url)
2650 {
2651 	// First test if the URL has a protocol field
2652 	int32 at = url.FindFirst(":");
2653 
2654 	if (at != B_ERROR) {
2655 		// There is a protocol, let's see if we can handle it
2656 		BString proto;
2657 		url.CopyInto(proto, 0, at);
2658 
2659 		bool handled = false;
2660 
2661 		// First try the built-in supported ones
2662 		for (unsigned int i = 0; i < sizeof(kHandledProtocols) / sizeof(char*);
2663 				i++) {
2664 			handled = (proto == kHandledProtocols[i]);
2665 			if (handled)
2666 				break;
2667 		}
2668 
2669 		if (handled) {
2670 			// This is the easy case, a complete and well-formed URL, we can
2671 			// navigate to it without further efforts.
2672 			_VisitURL(url);
2673 			return;
2674 		} else {
2675 			// There is what looks like a protocol, but one we don't know.
2676 			// Ask the BRoster if there is a matching filetype and app which
2677 			// can handle it.
2678 			BString temp;
2679 			temp = "application/x-vnd.Be.URL.";
2680 			temp += proto;
2681 
2682 			const char* argv[] = { url.String(), NULL };
2683 
2684 			if (be_roster->Launch(temp.String(), 1, argv) == B_OK)
2685 				return;
2686 		}
2687 	}
2688 
2689 	// There is no protocol or only an unsupported one. So let's try harder to
2690 	// guess what the request is.
2691 
2692 	// "localhost" is a special case, it is a valid domain name but has no dots.
2693 	// Handle it separately.
2694 	if (url == "localhost")
2695 		_VisitURL("http://localhost/");
2696 	else {
2697 		// Also handle URLs starting with "localhost" followed by a path.
2698 		const char* localhostPrefix = "localhost/";
2699 
2700 		if (url.Compare(localhostPrefix, strlen(localhostPrefix)) == 0)
2701 			_VisitURL(url);
2702 		else {
2703 			// In all other cases we try to detect a valid domain name. There
2704 			// must be at least one dot and no spaces until the first / in the
2705 			// URL.
2706 			bool isURL = false;
2707 
2708 			for (int32 i = 0; i < url.CountChars(); i++) {
2709 				if (url[i] == '.')
2710 					isURL = true;
2711 				else if (url[i] == '/')
2712 					break;
2713 				else if (!_IsValidDomainChar(url[i])) {
2714 					isURL = false;
2715 
2716 					break;
2717 				}
2718 			}
2719 
2720 			if (isURL) {
2721 				// This is apparently an URL missing the protocol part. In that
2722 				// case we default to http.
2723 				BString prefixed = "http://";
2724 				prefixed << url;
2725 				_VisitURL(prefixed);
2726 				return;
2727 			} else {
2728 				// We couldn't find anything that looks like an URL. Let's
2729 				// assume what we have is a search request and go to the search
2730 				// engine.
2731 				_VisitSearchEngine(url);
2732 			}
2733 		}
2734 	}
2735 }
2736 
2737 
2738 void
_HandlePageSourceResult(const BMessage * message)2739 BrowserWindow::_HandlePageSourceResult(const BMessage* message)
2740 {
2741 	// TODO: This should be done in an extra thread perhaps. Doing it in
2742 	// the application thread is not much better, since it actually draws
2743 	// the pages...
2744 
2745 	BPath pathToPageSource;
2746 
2747 	BString url;
2748 	status_t ret = message->FindString("url", &url);
2749 	if (ret == B_OK && url.FindFirst("file://") == 0) {
2750 		// Local file
2751 		url.Remove(0, strlen("file://"));
2752 		pathToPageSource.SetTo(url.String());
2753 	} else {
2754 		// Something else, store it.
2755 		// TODO: What if it isn't HTML, but for example SVG?
2756 		BString source;
2757 		ret = message->FindString("source", &source);
2758 
2759 		if (ret == B_OK)
2760 			ret = find_directory(B_SYSTEM_TEMP_DIRECTORY, &pathToPageSource);
2761 
2762 		BString tmpFileName("PageSource_");
2763 		tmpFileName << system_time() << ".html";
2764 		if (ret == B_OK)
2765 			ret = pathToPageSource.Append(tmpFileName.String());
2766 
2767 		BFile pageSourceFile(pathToPageSource.Path(),
2768 			B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
2769 		if (ret == B_OK)
2770 			ret = pageSourceFile.InitCheck();
2771 
2772 		if (ret == B_OK) {
2773 			ssize_t written = pageSourceFile.Write(source.String(),
2774 				source.Length());
2775 			if (written != source.Length())
2776 				ret = (status_t)written;
2777 		}
2778 
2779 		if (ret == B_OK) {
2780 			const char* type = "text/html";
2781 			size_t size = strlen(type);
2782 			pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size);
2783 				// If it fails we don't care.
2784 		}
2785 	}
2786 
2787 	entry_ref ref;
2788 	if (ret == B_OK)
2789 		ret = get_ref_for_path(pathToPageSource.Path(), &ref);
2790 
2791 	if (ret == B_OK) {
2792 		BMessage refsMessage(B_REFS_RECEIVED);
2793 		ret = refsMessage.AddRef("refs", &ref);
2794 		if (ret == B_OK) {
2795 			ret = be_roster->Launch("text/x-source-code", &refsMessage);
2796 			if (ret == B_ALREADY_RUNNING)
2797 				ret = B_OK;
2798 		}
2799 	}
2800 
2801 	if (ret != B_OK) {
2802 		char buffer[1024];
2803 		snprintf(buffer, sizeof(buffer), "Failed to show the "
2804 			"page source: %s\n", strerror(ret));
2805 		BAlert* alert = new BAlert(B_TRANSLATE("Page source error"), buffer,
2806 			B_TRANSLATE("OK"));
2807 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2808 		alert->Go(NULL);
2809 	}
2810 }
2811 
2812 
2813 void
_ShowBookmarkBar(bool show)2814 BrowserWindow::_ShowBookmarkBar(bool show)
2815 {
2816 	// It is not allowed to show the bookmark bar when it is empty
2817 	if (show && (fBookmarkBar == NULL || fBookmarkBar->CountItems() <= 1))
2818 	{
2819 		fBookmarkBarMenuItem->SetMarked(false);
2820 		return;
2821 	}
2822 
2823 	fBookmarkBarMenuItem->SetMarked(show);
2824 
2825 	if (fBookmarkBar == NULL || fBookmarkBar->IsHidden() != show)
2826 		return;
2827 
2828 	fAppSettings->SetValue(kSettingsShowBookmarkBar, show);
2829 
2830 	if (show)
2831 		fBookmarkBar->Show();
2832 	else
2833 		fBookmarkBar->Hide();
2834 }
2835