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